From 259d7dd4715ac68b3c7c39292d40fcdb8e1f12de Mon Sep 17 00:00:00 2001 From: Ahmed Ibrahim Date: Tue, 23 Dec 2025 11:50:14 -0800 Subject: [PATCH 1/8] remove --- .../app-server/tests/common/models_cache.rs | 2 - .../codex-api/tests/models_integration.rs | 2 - codex-rs/core/src/config/mod.rs | 12 --- .../core/src/models_manager/model_family.rs | 24 ------ codex-rs/core/tests/responses_headers.rs | 2 - codex-rs/core/tests/suite/remote_models.rs | 4 - codex-rs/protocol/src/openai_models.rs | 9 -- codex-rs/tui/src/chatwidget.rs | 7 +- codex-rs/tui/src/history_cell.rs | 81 +++++------------ codex-rs/tui2/src/chatwidget.rs | 7 +- codex-rs/tui2/src/history_cell.rs | 86 ++++++------------- 11 files changed, 54 insertions(+), 182 deletions(-) diff --git a/codex-rs/app-server/tests/common/models_cache.rs b/codex-rs/app-server/tests/common/models_cache.rs index b889f004b61..0a977aa7c28 100644 --- a/codex-rs/app-server/tests/common/models_cache.rs +++ b/codex-rs/app-server/tests/common/models_cache.rs @@ -5,7 +5,6 @@ use codex_protocol::openai_models::ConfigShellToolType; use codex_protocol::openai_models::ModelInfo; use codex_protocol::openai_models::ModelPreset; use codex_protocol::openai_models::ModelVisibility; -use codex_protocol::openai_models::ReasoningSummaryFormat; use codex_protocol::openai_models::TruncationPolicyConfig; use serde_json::json; use std::path::Path; @@ -35,7 +34,6 @@ fn preset_to_info(preset: &ModelPreset, priority: i32) -> ModelInfo { truncation_policy: TruncationPolicyConfig::bytes(10_000), supports_parallel_tool_calls: false, context_window: None, - reasoning_summary_format: ReasoningSummaryFormat::None, experimental_supported_tools: Vec::new(), } } diff --git a/codex-rs/codex-api/tests/models_integration.rs b/codex-rs/codex-api/tests/models_integration.rs index 115c5a829ee..f197e806fd9 100644 --- a/codex-rs/codex-api/tests/models_integration.rs +++ b/codex-rs/codex-api/tests/models_integration.rs @@ -10,7 +10,6 @@ use codex_protocol::openai_models::ModelVisibility; use codex_protocol::openai_models::ModelsResponse; use codex_protocol::openai_models::ReasoningEffort; use codex_protocol::openai_models::ReasoningEffortPreset; -use codex_protocol::openai_models::ReasoningSummaryFormat; use codex_protocol::openai_models::TruncationPolicyConfig; use http::HeaderMap; use http::Method; @@ -85,7 +84,6 @@ async fn models_client_hits_models_endpoint() { truncation_policy: TruncationPolicyConfig::bytes(10_000), supports_parallel_tool_calls: false, context_window: None, - reasoning_summary_format: ReasoningSummaryFormat::None, experimental_supported_tools: Vec::new(), }], etag: String::new(), diff --git a/codex-rs/core/src/config/mod.rs b/codex-rs/core/src/config/mod.rs index 90f5ca2e08c..53864851ae0 100644 --- a/codex-rs/core/src/config/mod.rs +++ b/codex-rs/core/src/config/mod.rs @@ -38,7 +38,6 @@ use codex_protocol::config_types::SandboxMode; use codex_protocol::config_types::TrustLevel; use codex_protocol::config_types::Verbosity; use codex_protocol::openai_models::ReasoningEffort; -use codex_protocol::openai_models::ReasoningSummaryFormat; use codex_rmcp_client::OAuthCredentialsStoreMode; use codex_utils_absolute_path::AbsolutePathBuf; use codex_utils_absolute_path::AbsolutePathBufGuard; @@ -303,9 +302,6 @@ pub struct Config { /// Optional override to force-enable reasoning summaries for the configured model. pub model_supports_reasoning_summaries: Option, - /// Optional override to force reasoning summary format for the configured model. - pub model_reasoning_summary_format: Option, - /// Optional verbosity control for GPT-5 models (Responses API `text.verbosity`). pub model_verbosity: Option, @@ -786,9 +782,6 @@ pub struct ConfigToml { /// Override to force-enable reasoning summaries for the configured model. pub model_supports_reasoning_summaries: Option, - /// Override to force reasoning summary format for the configured model. - pub model_reasoning_summary_format: Option, - /// Base URL for requests to ChatGPT (as opposed to the OpenAI API). pub chatgpt_base_url: Option, @@ -1379,7 +1372,6 @@ impl Config { .or(cfg.model_reasoning_summary) .unwrap_or_default(), model_supports_reasoning_summaries: cfg.model_supports_reasoning_summaries, - model_reasoning_summary_format: cfg.model_reasoning_summary_format.clone(), model_verbosity: config_profile.model_verbosity.or(cfg.model_verbosity), chatgpt_base_url: config_profile .chatgpt_base_url @@ -3189,7 +3181,6 @@ model_verbosity = "high" model_reasoning_effort: Some(ReasoningEffort::High), model_reasoning_summary: ReasoningSummary::Detailed, model_supports_reasoning_summaries: None, - model_reasoning_summary_format: None, model_verbosity: None, chatgpt_base_url: "https://chatgpt.com/backend-api/".to_string(), base_instructions: None, @@ -3273,7 +3264,6 @@ model_verbosity = "high" model_reasoning_effort: None, model_reasoning_summary: ReasoningSummary::default(), model_supports_reasoning_summaries: None, - model_reasoning_summary_format: None, model_verbosity: None, chatgpt_base_url: "https://chatgpt.com/backend-api/".to_string(), base_instructions: None, @@ -3372,7 +3362,6 @@ model_verbosity = "high" model_reasoning_effort: None, model_reasoning_summary: ReasoningSummary::default(), model_supports_reasoning_summaries: None, - model_reasoning_summary_format: None, model_verbosity: None, chatgpt_base_url: "https://chatgpt.com/backend-api/".to_string(), base_instructions: None, @@ -3457,7 +3446,6 @@ model_verbosity = "high" model_reasoning_effort: Some(ReasoningEffort::High), model_reasoning_summary: ReasoningSummary::Detailed, model_supports_reasoning_summaries: None, - model_reasoning_summary_format: None, model_verbosity: Some(Verbosity::High), chatgpt_base_url: "https://chatgpt.com/backend-api/".to_string(), base_instructions: None, diff --git a/codex-rs/core/src/models_manager/model_family.rs b/codex-rs/core/src/models_manager/model_family.rs index 9e2fb924685..9a6904cea1e 100644 --- a/codex-rs/core/src/models_manager/model_family.rs +++ b/codex-rs/core/src/models_manager/model_family.rs @@ -3,7 +3,6 @@ use codex_protocol::openai_models::ApplyPatchToolType; use codex_protocol::openai_models::ConfigShellToolType; use codex_protocol::openai_models::ModelInfo; use codex_protocol::openai_models::ReasoningEffort; -use codex_protocol::openai_models::ReasoningSummaryFormat; use crate::config::Config; use crate::truncate::TruncationPolicy; @@ -48,9 +47,6 @@ pub struct ModelFamily { // The reasoning effort to use for this model family when none is explicitly chosen. pub default_reasoning_effort: Option, - // Define if we need a special handling of reasoning summary - pub reasoning_summary_format: ReasoningSummaryFormat, - /// Whether this model supports parallel tool calls when using the /// Responses API. pub supports_parallel_tool_calls: bool, @@ -88,9 +84,6 @@ impl ModelFamily { if let Some(supports_reasoning_summaries) = config.model_supports_reasoning_summaries { self.supports_reasoning_summaries = supports_reasoning_summaries; } - if let Some(reasoning_summary_format) = config.model_reasoning_summary_format.as_ref() { - self.reasoning_summary_format = reasoning_summary_format.clone(); - } if let Some(context_window) = config.model_context_window { self.context_window = Some(context_window); } @@ -128,7 +121,6 @@ impl ModelFamily { truncation_policy, supports_parallel_tool_calls, context_window, - reasoning_summary_format, experimental_supported_tools, } = model; @@ -144,7 +136,6 @@ impl ModelFamily { self.truncation_policy = truncation_policy.into(); self.supports_parallel_tool_calls = supports_parallel_tool_calls; self.context_window = context_window; - self.reasoning_summary_format = reasoning_summary_format; self.experimental_supported_tools = experimental_supported_tools; } @@ -175,7 +166,6 @@ macro_rules! model_family { context_window: Some(CONTEXT_WINDOW_272K), auto_compact_token_limit: None, supports_reasoning_summaries: false, - reasoning_summary_format: ReasoningSummaryFormat::None, supports_parallel_tool_calls: false, apply_patch_tool_type: None, base_instructions: BASE_INSTRUCTIONS.to_string(), @@ -250,7 +240,6 @@ pub(super) fn find_family_for_model(slug: &str) -> ModelFamily { model_family!( slug, slug, supports_reasoning_summaries: true, - reasoning_summary_format: ReasoningSummaryFormat::Experimental, base_instructions: GPT_5_CODEX_INSTRUCTIONS.to_string(), experimental_supported_tools: vec![ "grep_files".to_string(), @@ -270,7 +259,6 @@ pub(super) fn find_family_for_model(slug: &str) -> ModelFamily { model_family!( slug, slug, supports_reasoning_summaries: true, - reasoning_summary_format: ReasoningSummaryFormat::Experimental, base_instructions: GPT_5_2_CODEX_INSTRUCTIONS.to_string(), apply_patch_tool_type: Some(ApplyPatchToolType::Freeform), shell_type: ConfigShellToolType::ShellCommand, @@ -299,7 +287,6 @@ pub(super) fn find_family_for_model(slug: &str) -> ModelFamily { model_family!( slug, slug, supports_reasoning_summaries: true, - reasoning_summary_format: ReasoningSummaryFormat::Experimental, base_instructions: GPT_5_2_CODEX_INSTRUCTIONS.to_string(), apply_patch_tool_type: Some(ApplyPatchToolType::Freeform), shell_type: ConfigShellToolType::ShellCommand, @@ -312,7 +299,6 @@ pub(super) fn find_family_for_model(slug: &str) -> ModelFamily { model_family!( slug, slug, supports_reasoning_summaries: true, - reasoning_summary_format: ReasoningSummaryFormat::Experimental, base_instructions: GPT_5_2_CODEX_INSTRUCTIONS.to_string(), apply_patch_tool_type: Some(ApplyPatchToolType::Freeform), shell_type: ConfigShellToolType::ShellCommand, @@ -325,7 +311,6 @@ pub(super) fn find_family_for_model(slug: &str) -> ModelFamily { model_family!( slug, slug, supports_reasoning_summaries: true, - reasoning_summary_format: ReasoningSummaryFormat::Experimental, base_instructions: GPT_5_1_CODEX_MAX_INSTRUCTIONS.to_string(), apply_patch_tool_type: Some(ApplyPatchToolType::Freeform), shell_type: ConfigShellToolType::ShellCommand, @@ -341,7 +326,6 @@ pub(super) fn find_family_for_model(slug: &str) -> ModelFamily { model_family!( slug, slug, supports_reasoning_summaries: true, - reasoning_summary_format: ReasoningSummaryFormat::Experimental, base_instructions: GPT_5_CODEX_INSTRUCTIONS.to_string(), apply_patch_tool_type: Some(ApplyPatchToolType::Freeform), shell_type: ConfigShellToolType::ShellCommand, @@ -416,7 +400,6 @@ fn derive_default_model_family(model: &str) -> ModelFamily { context_window: None, auto_compact_token_limit: None, supports_reasoning_summaries: false, - reasoning_summary_format: ReasoningSummaryFormat::None, supports_parallel_tool_calls: false, apply_patch_tool_type: None, base_instructions: BASE_INSTRUCTIONS.to_string(), @@ -460,7 +443,6 @@ mod tests { truncation_policy: TruncationPolicyConfig::bytes(10_000), supports_parallel_tool_calls: false, context_window: None, - reasoning_summary_format: ReasoningSummaryFormat::None, experimental_supported_tools: Vec::new(), } } @@ -524,7 +506,6 @@ mod tests { experimental_supported_tools: vec!["local".to_string()], truncation_policy: TruncationPolicy::Bytes(10_000), context_window: Some(100), - reasoning_summary_format: ReasoningSummaryFormat::None, ); let updated = family.with_remote_overrides(vec![ModelInfo { @@ -549,7 +530,6 @@ mod tests { truncation_policy: TruncationPolicyConfig::tokens(2_000), supports_parallel_tool_calls: true, context_window: Some(400_000), - reasoning_summary_format: ReasoningSummaryFormat::Experimental, experimental_supported_tools: vec!["alpha".to_string(), "beta".to_string()], }]); @@ -568,10 +548,6 @@ mod tests { assert_eq!(updated.truncation_policy, TruncationPolicy::Tokens(2_000)); assert!(updated.supports_parallel_tool_calls); assert_eq!(updated.context_window, Some(400_000)); - assert_eq!( - updated.reasoning_summary_format, - ReasoningSummaryFormat::Experimental - ); assert_eq!( updated.experimental_supported_tools, vec!["alpha".to_string(), "beta".to_string()] diff --git a/codex-rs/core/tests/responses_headers.rs b/codex-rs/core/tests/responses_headers.rs index c406fdbc879..3b0ffd2983c 100644 --- a/codex-rs/core/tests/responses_headers.rs +++ b/codex-rs/core/tests/responses_headers.rs @@ -14,7 +14,6 @@ use codex_core::models_manager::manager::ModelsManager; use codex_otel::otel_manager::OtelManager; use codex_protocol::ConversationId; use codex_protocol::config_types::ReasoningSummary; -use codex_protocol::openai_models::ReasoningSummaryFormat; use codex_protocol::protocol::SessionSource; use codex_protocol::protocol::SubAgentSource; use core_test_support::load_default_config_for_test; @@ -246,7 +245,6 @@ async fn responses_respects_model_family_overrides_from_config() { config.model_provider_id = provider.name.clone(); config.model_provider = provider.clone(); config.model_supports_reasoning_summaries = Some(true); - config.model_reasoning_summary_format = Some(ReasoningSummaryFormat::Experimental); config.model_reasoning_summary = ReasoningSummary::Detailed; let effort = config.model_reasoning_effort; let summary = config.model_reasoning_summary; diff --git a/codex-rs/core/tests/suite/remote_models.rs b/codex-rs/core/tests/suite/remote_models.rs index 15a78660982..7f2719776f7 100644 --- a/codex-rs/core/tests/suite/remote_models.rs +++ b/codex-rs/core/tests/suite/remote_models.rs @@ -24,7 +24,6 @@ use codex_protocol::openai_models::ModelVisibility; use codex_protocol::openai_models::ModelsResponse; use codex_protocol::openai_models::ReasoningEffort; use codex_protocol::openai_models::ReasoningEffortPreset; -use codex_protocol::openai_models::ReasoningSummaryFormat; use codex_protocol::openai_models::TruncationPolicyConfig; use codex_protocol::user_input::UserInput; use core_test_support::load_default_config_for_test; @@ -83,7 +82,6 @@ async fn remote_models_remote_model_uses_unified_exec() -> Result<()> { truncation_policy: TruncationPolicyConfig::bytes(10_000), supports_parallel_tool_calls: false, context_window: None, - reasoning_summary_format: ReasoningSummaryFormat::None, experimental_supported_tools: Vec::new(), }; @@ -222,7 +220,6 @@ async fn remote_models_apply_remote_base_instructions() -> Result<()> { truncation_policy: TruncationPolicyConfig::bytes(10_000), supports_parallel_tool_calls: false, context_window: None, - reasoning_summary_format: ReasoningSummaryFormat::None, experimental_supported_tools: Vec::new(), }; mount_models_once( @@ -486,7 +483,6 @@ fn test_remote_model(slug: &str, visibility: ModelVisibility, priority: i32) -> truncation_policy: TruncationPolicyConfig::bytes(10_000), supports_parallel_tool_calls: false, context_window: None, - reasoning_summary_format: ReasoningSummaryFormat::None, experimental_supported_tools: Vec::new(), } } diff --git a/codex-rs/protocol/src/openai_models.rs b/codex-rs/protocol/src/openai_models.rs index fc7f0b8ce02..f2fc08fcdb2 100644 --- a/codex-rs/protocol/src/openai_models.rs +++ b/codex-rs/protocol/src/openai_models.rs @@ -125,14 +125,6 @@ pub enum ApplyPatchToolType { Function, } -#[derive(Deserialize, Debug, Clone, PartialEq, Eq, Default, Hash, TS, JsonSchema, Serialize)] -#[serde(rename_all = "snake_case")] -pub enum ReasoningSummaryFormat { - #[default] - None, - Experimental, -} - /// Server-provided truncation policy metadata for a model. #[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, TS, JsonSchema)] #[serde(rename_all = "snake_case")] @@ -188,7 +180,6 @@ pub struct ModelInfo { pub truncation_policy: TruncationPolicyConfig, pub supports_parallel_tool_calls: bool, pub context_window: Option, - pub reasoning_summary_format: ReasoningSummaryFormat, pub experimental_supported_tools: Vec, } diff --git a/codex-rs/tui/src/chatwidget.rs b/codex-rs/tui/src/chatwidget.rs index aad4e44ad82..a11096fbafb 100644 --- a/codex-rs/tui/src/chatwidget.rs +++ b/codex-rs/tui/src/chatwidget.rs @@ -530,14 +530,11 @@ impl ChatWidget { } fn on_agent_reasoning_final(&mut self) { - let reasoning_summary_format = self.get_model_family().reasoning_summary_format; // At the end of a reasoning block, record transcript-only content. self.full_reasoning_buffer.push_str(&self.reasoning_buffer); if !self.full_reasoning_buffer.is_empty() { - let cell = history_cell::new_reasoning_summary_block( - self.full_reasoning_buffer.clone(), - reasoning_summary_format, - ); + let cell = + history_cell::new_reasoning_summary_block(self.full_reasoning_buffer.clone()); self.add_boxed_history(cell); } self.reasoning_buffer.clear(); diff --git a/codex-rs/tui/src/history_cell.rs b/codex-rs/tui/src/history_cell.rs index 0b768dfb20f..b487c6aeff5 100644 --- a/codex-rs/tui/src/history_cell.rs +++ b/codex-rs/tui/src/history_cell.rs @@ -33,7 +33,6 @@ use codex_core::protocol::McpAuthStatus; use codex_core::protocol::McpInvocation; use codex_core::protocol::SessionConfiguredEvent; use codex_protocol::openai_models::ReasoningEffort as ReasoningEffortConfig; -use codex_protocol::openai_models::ReasoningSummaryFormat; use codex_protocol::plan_tool::PlanItemArg; use codex_protocol::plan_tool::StepStatus; use codex_protocol::plan_tool::UpdatePlanArgs; @@ -1661,39 +1660,28 @@ pub(crate) fn new_view_image_tool_call(path: PathBuf, cwd: &Path) -> PlainHistor PlainHistoryCell { lines } } -pub(crate) fn new_reasoning_summary_block( - full_reasoning_buffer: String, - reasoning_summary_format: ReasoningSummaryFormat, -) -> Box { - if reasoning_summary_format == ReasoningSummaryFormat::Experimental { - // Experimental format is following: - // ** header ** - // - // reasoning summary - // - // So we need to strip header from reasoning summary - let full_reasoning_buffer = full_reasoning_buffer.trim(); - if let Some(open) = full_reasoning_buffer.find("**") { - let after_open = &full_reasoning_buffer[(open + 2)..]; - if let Some(close) = after_open.find("**") { - let after_close_idx = open + 2 + close + 2; - // if we don't have anything beyond `after_close_idx` - // then we don't have a summary to inject into history - if after_close_idx < full_reasoning_buffer.len() { - let header_buffer = full_reasoning_buffer[..after_close_idx].to_string(); - let summary_buffer = full_reasoning_buffer[after_close_idx..].to_string(); - return Box::new(ReasoningSummaryCell::new( - header_buffer, - summary_buffer, - false, - )); - } +pub(crate) fn new_reasoning_summary_block(full_reasoning_buffer: String) -> Box { + let full_reasoning_buffer = full_reasoning_buffer.trim(); + if let Some(open) = full_reasoning_buffer.find("**") { + let after_open = &full_reasoning_buffer[(open + 2)..]; + if let Some(close) = after_open.find("**") { + let after_close_idx = open + 2 + close + 2; + // if we don't have anything beyond `after_close_idx` + // then we don't have a summary to inject into history + if after_close_idx < full_reasoning_buffer.len() { + let header_buffer = full_reasoning_buffer[..after_close_idx].to_string(); + let summary_buffer = full_reasoning_buffer[after_close_idx..].to_string(); + return Box::new(ReasoningSummaryCell::new( + header_buffer, + summary_buffer, + false, + )); } } } Box::new(ReasoningSummaryCell::new( "".to_string(), - full_reasoning_buffer, + full_reasoning_buffer.to_string(), true, )) } @@ -1759,7 +1747,6 @@ mod tests { use codex_core::config::ConfigBuilder; use codex_core::config::types::McpServerConfig; use codex_core::config::types::McpServerTransportConfig; - use codex_core::models_manager::manager::ModelsManager; use codex_core::protocol::McpAuthStatus; use codex_protocol::parse_command::ParsedCommand; use dirs::home_dir; @@ -2672,10 +2659,8 @@ mod tests { } #[test] fn reasoning_summary_block() { - let reasoning_format = ReasoningSummaryFormat::Experimental; let cell = new_reasoning_summary_block( "**High level reasoning**\n\nDetailed reasoning goes here.".to_string(), - reasoning_format, ); let rendered_display = render_lines(&cell.display_lines(80)); @@ -2687,11 +2672,7 @@ mod tests { #[test] fn reasoning_summary_block_returns_reasoning_cell_when_feature_disabled() { - let reasoning_format = ReasoningSummaryFormat::Experimental; - let cell = new_reasoning_summary_block( - "Detailed reasoning goes here.".to_string(), - reasoning_format, - ); + let cell = new_reasoning_summary_block("Detailed reasoning goes here.".to_string()); let rendered = render_transcript(cell.as_ref()); assert_eq!(rendered, vec!["• Detailed reasoning goes here."]); @@ -2702,17 +2683,8 @@ mod tests { let mut config = test_config().await; config.model = Some("gpt-3.5-turbo".to_string()); config.model_supports_reasoning_summaries = Some(true); - config.model_reasoning_summary_format = Some(ReasoningSummaryFormat::Experimental); - let model_family = - ModelsManager::construct_model_family_offline(&config.model.clone().unwrap(), &config); - assert_eq!( - model_family.reasoning_summary_format, - ReasoningSummaryFormat::Experimental - ); - let cell = new_reasoning_summary_block( "**High level reasoning**\n\nDetailed reasoning goes here.".to_string(), - model_family.reasoning_summary_format, ); let rendered_display = render_lines(&cell.display_lines(80)); @@ -2721,11 +2693,8 @@ mod tests { #[test] fn reasoning_summary_block_falls_back_when_header_is_missing() { - let reasoning_format = ReasoningSummaryFormat::Experimental; - let cell = new_reasoning_summary_block( - "**High level reasoning without closing".to_string(), - reasoning_format, - ); + let cell = + new_reasoning_summary_block("**High level reasoning without closing".to_string()); let rendered = render_transcript(cell.as_ref()); assert_eq!(rendered, vec!["• **High level reasoning without closing"]); @@ -2733,18 +2702,14 @@ mod tests { #[test] fn reasoning_summary_block_falls_back_when_summary_is_missing() { - let reasoning_format = ReasoningSummaryFormat::Experimental; - let cell = new_reasoning_summary_block( - "**High level reasoning without closing**".to_string(), - reasoning_format.clone(), - ); + let cell = + new_reasoning_summary_block("**High level reasoning without closing**".to_string()); let rendered = render_transcript(cell.as_ref()); assert_eq!(rendered, vec!["• High level reasoning without closing"]); let cell = new_reasoning_summary_block( "**High level reasoning without closing**\n\n ".to_string(), - reasoning_format, ); let rendered = render_transcript(cell.as_ref()); @@ -2753,10 +2718,8 @@ mod tests { #[test] fn reasoning_summary_block_splits_header_and_summary_when_present() { - let reasoning_format = ReasoningSummaryFormat::Experimental; let cell = new_reasoning_summary_block( "**High level plan**\n\nWe should fix the bug next.".to_string(), - reasoning_format, ); let rendered_display = render_lines(&cell.display_lines(80)); diff --git a/codex-rs/tui2/src/chatwidget.rs b/codex-rs/tui2/src/chatwidget.rs index ee0dd75ddb6..1b521b64a37 100644 --- a/codex-rs/tui2/src/chatwidget.rs +++ b/codex-rs/tui2/src/chatwidget.rs @@ -496,14 +496,11 @@ impl ChatWidget { } fn on_agent_reasoning_final(&mut self) { - let reasoning_summary_format = self.get_model_family().reasoning_summary_format; // At the end of a reasoning block, record transcript-only content. self.full_reasoning_buffer.push_str(&self.reasoning_buffer); if !self.full_reasoning_buffer.is_empty() { - let cell = history_cell::new_reasoning_summary_block( - self.full_reasoning_buffer.clone(), - reasoning_summary_format, - ); + let cell = + history_cell::new_reasoning_summary_block(self.full_reasoning_buffer.clone()); self.add_boxed_history(cell); } self.reasoning_buffer.clear(); diff --git a/codex-rs/tui2/src/history_cell.rs b/codex-rs/tui2/src/history_cell.rs index f9fd36b92eb..ae35db6ed8a 100644 --- a/codex-rs/tui2/src/history_cell.rs +++ b/codex-rs/tui2/src/history_cell.rs @@ -31,7 +31,6 @@ use codex_core::protocol::McpAuthStatus; use codex_core::protocol::McpInvocation; use codex_core::protocol::SessionConfiguredEvent; use codex_protocol::openai_models::ReasoningEffort as ReasoningEffortConfig; -use codex_protocol::openai_models::ReasoningSummaryFormat; use codex_protocol::plan_tool::PlanItemArg; use codex_protocol::plan_tool::StepStatus; use codex_protocol::plan_tool::UpdatePlanArgs; @@ -1419,39 +1418,34 @@ pub(crate) fn new_view_image_tool_call(path: PathBuf, cwd: &Path) -> PlainHistor PlainHistoryCell { lines } } -pub(crate) fn new_reasoning_summary_block( - full_reasoning_buffer: String, - reasoning_summary_format: ReasoningSummaryFormat, -) -> Box { - if reasoning_summary_format == ReasoningSummaryFormat::Experimental { - // Experimental format is following: - // ** header ** - // - // reasoning summary - // - // So we need to strip header from reasoning summary - let full_reasoning_buffer = full_reasoning_buffer.trim(); - if let Some(open) = full_reasoning_buffer.find("**") { - let after_open = &full_reasoning_buffer[(open + 2)..]; - if let Some(close) = after_open.find("**") { - let after_close_idx = open + 2 + close + 2; - // if we don't have anything beyond `after_close_idx` - // then we don't have a summary to inject into history - if after_close_idx < full_reasoning_buffer.len() { - let header_buffer = full_reasoning_buffer[..after_close_idx].to_string(); - let summary_buffer = full_reasoning_buffer[after_close_idx..].to_string(); - return Box::new(ReasoningSummaryCell::new( - header_buffer, - summary_buffer, - false, - )); - } +pub(crate) fn new_reasoning_summary_block(full_reasoning_buffer: String) -> Box { + // Experimental format is following: + // ** header ** + // + // reasoning summary + // + // So we need to strip header from reasoning summary + let full_reasoning_buffer = full_reasoning_buffer.trim(); + if let Some(open) = full_reasoning_buffer.find("**") { + let after_open = &full_reasoning_buffer[(open + 2)..]; + if let Some(close) = after_open.find("**") { + let after_close_idx = open + 2 + close + 2; + // if we don't have anything beyond `after_close_idx` + // then we don't have a summary to inject into history + if after_close_idx < full_reasoning_buffer.len() { + let header_buffer = full_reasoning_buffer[..after_close_idx].to_string(); + let summary_buffer = full_reasoning_buffer[after_close_idx..].to_string(); + return Box::new(ReasoningSummaryCell::new( + header_buffer, + summary_buffer, + false, + )); } } } Box::new(ReasoningSummaryCell::new( "".to_string(), - full_reasoning_buffer, + full_reasoning_buffer.to_string(), true, )) } @@ -1517,7 +1511,6 @@ mod tests { use codex_core::config::ConfigBuilder; use codex_core::config::types::McpServerConfig; use codex_core::config::types::McpServerTransportConfig; - use codex_core::models_manager::manager::ModelsManager; use codex_core::protocol::McpAuthStatus; use codex_protocol::parse_command::ParsedCommand; use dirs::home_dir; @@ -2364,10 +2357,8 @@ mod tests { } #[test] fn reasoning_summary_block() { - let reasoning_format = ReasoningSummaryFormat::Experimental; let cell = new_reasoning_summary_block( "**High level reasoning**\n\nDetailed reasoning goes here.".to_string(), - reasoning_format, ); let rendered_display = render_lines(&cell.display_lines(80)); @@ -2379,11 +2370,7 @@ mod tests { #[test] fn reasoning_summary_block_returns_reasoning_cell_when_feature_disabled() { - let reasoning_format = ReasoningSummaryFormat::Experimental; - let cell = new_reasoning_summary_block( - "Detailed reasoning goes here.".to_string(), - reasoning_format, - ); + let cell = new_reasoning_summary_block("Detailed reasoning goes here.".to_string()); let rendered = render_transcript(cell.as_ref()); assert_eq!(rendered, vec!["• Detailed reasoning goes here."]); @@ -2394,17 +2381,9 @@ mod tests { let mut config = test_config().await; config.model = Some("gpt-3.5-turbo".to_string()); config.model_supports_reasoning_summaries = Some(true); - config.model_reasoning_summary_format = Some(ReasoningSummaryFormat::Experimental); - let model_family = - ModelsManager::construct_model_family_offline(&config.model.clone().unwrap(), &config); - assert_eq!( - model_family.reasoning_summary_format, - ReasoningSummaryFormat::Experimental - ); let cell = new_reasoning_summary_block( "**High level reasoning**\n\nDetailed reasoning goes here.".to_string(), - model_family.reasoning_summary_format, ); let rendered_display = render_lines(&cell.display_lines(80)); @@ -2413,11 +2392,8 @@ mod tests { #[test] fn reasoning_summary_block_falls_back_when_header_is_missing() { - let reasoning_format = ReasoningSummaryFormat::Experimental; - let cell = new_reasoning_summary_block( - "**High level reasoning without closing".to_string(), - reasoning_format, - ); + let cell = + new_reasoning_summary_block("**High level reasoning without closing".to_string()); let rendered = render_transcript(cell.as_ref()); assert_eq!(rendered, vec!["• **High level reasoning without closing"]); @@ -2425,18 +2401,14 @@ mod tests { #[test] fn reasoning_summary_block_falls_back_when_summary_is_missing() { - let reasoning_format = ReasoningSummaryFormat::Experimental; - let cell = new_reasoning_summary_block( - "**High level reasoning without closing**".to_string(), - reasoning_format.clone(), - ); + let cell = + new_reasoning_summary_block("**High level reasoning without closing**".to_string()); let rendered = render_transcript(cell.as_ref()); assert_eq!(rendered, vec!["• High level reasoning without closing"]); let cell = new_reasoning_summary_block( "**High level reasoning without closing**\n\n ".to_string(), - reasoning_format, ); let rendered = render_transcript(cell.as_ref()); @@ -2445,10 +2417,8 @@ mod tests { #[test] fn reasoning_summary_block_splits_header_and_summary_when_present() { - let reasoning_format = ReasoningSummaryFormat::Experimental; let cell = new_reasoning_summary_block( "**High level plan**\n\nWe should fix the bug next.".to_string(), - reasoning_format, ); let rendered_display = render_lines(&cell.display_lines(80)); From 81b47933ef663488f17cd6923e7dfa183e95b1eb Mon Sep 17 00:00:00 2001 From: Ahmed Ibrahim Date: Tue, 23 Dec 2025 13:19:47 -0800 Subject: [PATCH 2/8] remove-model-family-2 --- codex-rs/tui/src/app.rs | 23 ++++------- codex-rs/tui/src/app_backtrack.rs | 4 +- codex-rs/tui/src/chatwidget.rs | 62 +++++++++++----------------- codex-rs/tui/src/chatwidget/tests.rs | 9 ++-- codex-rs/tui/src/status/card.rs | 47 +-------------------- codex-rs/tui/src/status/tests.rs | 54 ------------------------ 6 files changed, 38 insertions(+), 161 deletions(-) diff --git a/codex-rs/tui/src/app.rs b/codex-rs/tui/src/app.rs index c91eafcbe76..e0af51e7ebb 100644 --- a/codex-rs/tui/src/app.rs +++ b/codex-rs/tui/src/app.rs @@ -372,10 +372,6 @@ impl App { } let enhanced_keys_supported = tui.enhanced_keys_supported(); - let model_family = conversation_manager - .get_models_manager() - .construct_model_family(model.as_str(), &config) - .await; let mut chat_widget = match resume_selection { ResumeSelection::StartFresh | ResumeSelection::Exit => { let init = crate::chatwidget::ChatWidgetInit { @@ -389,7 +385,7 @@ impl App { models_manager: conversation_manager.get_models_manager(), feedback: feedback.clone(), is_first_run, - model_family: model_family.clone(), + model: model.clone(), }; ChatWidget::new(init, conversation_manager.clone()) } @@ -415,7 +411,7 @@ impl App { models_manager: conversation_manager.get_models_manager(), feedback: feedback.clone(), is_first_run, - model_family: model_family.clone(), + model: model.clone(), }; ChatWidget::new_from_existing( init, @@ -582,7 +578,7 @@ impl App { models_manager: self.server.get_models_manager(), feedback: self.feedback.clone(), is_first_run: false, - model_family: model_family.clone(), + model: self.current_model.clone(), }; self.chat_widget = ChatWidget::new(init, self.server.clone()); self.current_model = model_family.get_model_slug().to_string(); @@ -632,7 +628,7 @@ impl App { models_manager: self.server.get_models_manager(), feedback: self.feedback.clone(), is_first_run: false, - model_family: model_family.clone(), + model: self.current_model.clone(), }; self.chat_widget = ChatWidget::new_from_existing( init, @@ -767,12 +763,7 @@ impl App { self.on_update_reasoning_effort(effort); } AppEvent::UpdateModel(model) => { - let model_family = self - .server - .get_models_manager() - .construct_model_family(&model, &self.config) - .await; - self.chat_widget.set_model(&model, model_family); + self.chat_widget.set_model(&model); self.current_model = model; } AppEvent::OpenReasoningPopup { model } => { @@ -1357,7 +1348,7 @@ mod tests { async fn make_test_app() -> App { let (chat_widget, app_event_tx, _rx, _op_rx) = make_chatwidget_manual_with_sender().await; let config = chat_widget.config_ref().clone(); - let current_model = chat_widget.get_model_family().get_model_slug().to_string(); + let current_model = "gpt-5.2-codex".to_string(); let server = Arc::new(ConversationManager::with_models_provider( CodexAuth::from_api_key("Test API Key"), config.model_provider.clone(), @@ -1396,7 +1387,7 @@ mod tests { ) { let (chat_widget, app_event_tx, rx, op_rx) = make_chatwidget_manual_with_sender().await; let config = chat_widget.config_ref().clone(); - let current_model = chat_widget.get_model_family().get_model_slug().to_string(); + let current_model = "gpt-5.2-codex".to_string(); let server = Arc::new(ConversationManager::with_models_provider( CodexAuth::from_api_key("Test API Key"), config.model_provider.clone(), diff --git a/codex-rs/tui/src/app_backtrack.rs b/codex-rs/tui/src/app_backtrack.rs index 671702d3082..ce5dff2ed85 100644 --- a/codex-rs/tui/src/app_backtrack.rs +++ b/codex-rs/tui/src/app_backtrack.rs @@ -338,10 +338,9 @@ impl App { ) { let conv = new_conv.conversation; let session_configured = new_conv.session_configured; - let model_family = self.chat_widget.get_model_family(); let init = crate::chatwidget::ChatWidgetInit { config: cfg, - model_family: model_family.clone(), + model: self.current_model.clone(), frame_requester: tui.frame_requester(), app_event_tx: self.app_event_tx.clone(), initial_prompt: None, @@ -354,7 +353,6 @@ impl App { }; self.chat_widget = crate::chatwidget::ChatWidget::new_from_existing(init, conv, session_configured); - self.current_model = model_family.get_model_slug().to_string(); // Trim transcript up to the selected user message and re-render it. self.trim_transcript_for_backtrack(nth_user_message); self.render_transcript_once(tui); diff --git a/codex-rs/tui/src/chatwidget.rs b/codex-rs/tui/src/chatwidget.rs index a11096fbafb..21698b8dfbd 100644 --- a/codex-rs/tui/src/chatwidget.rs +++ b/codex-rs/tui/src/chatwidget.rs @@ -15,7 +15,6 @@ use codex_core::features::Feature; use codex_core::git_info::current_branch_name; use codex_core::git_info::local_git_branches; use codex_core::models_manager::manager::ModelsManager; -use codex_core::models_manager::model_family::ModelFamily; use codex_core::project_doc::DEFAULT_PROJECT_DOC_FILENAME; use codex_core::protocol::AgentMessageDeltaEvent; use codex_core::protocol::AgentMessageEvent; @@ -291,7 +290,7 @@ pub(crate) struct ChatWidgetInit { pub(crate) models_manager: Arc, pub(crate) feedback: codex_feedback::CodexFeedback, pub(crate) is_first_run: bool, - pub(crate) model_family: ModelFamily, + pub(crate) model: String, } #[derive(Default)] @@ -316,7 +315,7 @@ pub(crate) struct ChatWidget { bottom_pane: BottomPane, active_cell: Option>, config: Config, - model_family: ModelFamily, + model: String, auth_manager: Arc, models_manager: Arc, session_header: SessionHeader, @@ -601,12 +600,10 @@ impl ChatWidget { } fn context_remaining_percent(&self, info: &TokenUsageInfo) -> Option { - info.model_context_window - .or(self.model_family.context_window) - .map(|window| { - info.last_token_usage - .percent_of_context_window_remaining(window) - }) + info.model_context_window.map(|window| { + info.last_token_usage + .percent_of_context_window_remaining(window) + }) } fn context_used_tokens(&self, info: &TokenUsageInfo, percent_known: bool) -> Option { @@ -674,7 +671,7 @@ impl ChatWidget { if high_usage && !self.rate_limit_switch_prompt_hidden() - && self.model_family.get_model_slug() != NUDGE_MODEL_SLUG + && self.model != NUDGE_MODEL_SLUG && !matches!( self.rate_limit_switch_prompt, RateLimitSwitchPromptState::Shown @@ -708,9 +705,6 @@ impl ChatWidget { self.stream_controller = None; self.maybe_show_pending_rate_limit_prompt(); } - pub(crate) fn get_model_family(&self) -> ModelFamily { - self.model_family.clone() - } fn on_error(&mut self, message: String) { self.finalize_turn(); @@ -1413,11 +1407,10 @@ impl ChatWidget { models_manager, feedback, is_first_run, - model_family, + model, } = common; - let model_slug = model_family.get_model_slug().to_string(); let mut config = config; - config.model = Some(model_slug.clone()); + config.model = Some(model.clone()); let mut rng = rand::rng(); let placeholder = EXAMPLE_PROMPTS[rng.random_range(0..EXAMPLE_PROMPTS.len())].to_string(); let codex_op_tx = spawn_agent(config.clone(), app_event_tx.clone(), conversation_manager); @@ -1438,10 +1431,10 @@ impl ChatWidget { }), active_cell: None, config, - model_family, + model: model.clone(), auth_manager, models_manager, - session_header: SessionHeader::new(model_slug), + session_header: SessionHeader::new(model), initial_user_message: create_initial_user_message( initial_prompt.unwrap_or_default(), initial_images, @@ -1499,10 +1492,9 @@ impl ChatWidget { auth_manager, models_manager, feedback, - model_family, + model, .. } = common; - let model_slug = model_family.get_model_slug().to_string(); let mut rng = rand::rng(); let placeholder = EXAMPLE_PROMPTS[rng.random_range(0..EXAMPLE_PROMPTS.len())].to_string(); @@ -1525,10 +1517,10 @@ impl ChatWidget { }), active_cell: None, config, - model_family, + model: model.clone(), auth_manager, models_manager, - session_header: SessionHeader::new(model_slug), + session_header: SessionHeader::new(model), initial_user_message: create_initial_user_message( initial_prompt.unwrap_or_default(), initial_images, @@ -2240,22 +2232,20 @@ impl ChatWidget { pub(crate) fn add_status_output(&mut self) { let default_usage = TokenUsage::default(); - let (total_usage, context_usage) = if let Some(ti) = &self.token_info { - (&ti.total_token_usage, Some(&ti.last_token_usage)) + let total_usage = if let Some(ti) = &self.token_info { + &ti.total_token_usage } else { - (&default_usage, Some(&default_usage)) + &default_usage }; self.add_to_history(crate::status::new_status_output( &self.config, self.auth_manager.as_ref(), - &self.model_family, total_usage, - context_usage, &self.conversation_id, self.rate_limit_snapshot.as_ref(), self.plan_type, Local::now(), - self.model_family.get_model_slug(), + &self.model, )); } @@ -2408,7 +2398,6 @@ impl ChatWidget { /// Open a popup to choose a quick auto model. Selecting "All models" /// opens the full picker with every available preset. pub(crate) fn open_model_popup(&mut self) { - let current_model = self.model_family.get_model_slug().to_string(); let presets: Vec = // todo(aibrahim): make this async function match self.models_manager.try_list_models(&self.config) { @@ -2425,9 +2414,9 @@ impl ChatWidget { let current_label = presets .iter() - .find(|preset| preset.model == current_model) + .find(|preset| preset.model == self.model) .map(|preset| preset.display_name.to_string()) - .unwrap_or_else(|| current_model.clone()); + .unwrap_or_else(|| self.model.clone()); let (mut auto_presets, other_presets): (Vec, Vec) = presets .into_iter() @@ -2453,7 +2442,7 @@ impl ChatWidget { SelectionItem { name: preset.display_name.clone(), description, - is_current: model == current_model, + is_current: model == self.model, is_default: preset.is_default, actions, dismiss_on_select: true, @@ -2516,12 +2505,11 @@ impl ChatWidget { return; } - let current_model = self.model_family.get_model_slug().to_string(); let mut items: Vec = Vec::new(); for preset in presets.into_iter() { let description = (!preset.description.is_empty()).then_some(preset.description.to_string()); - let is_current = preset.model == current_model; + let is_current = preset.model == self.model; let single_supported_effort = preset.supported_reasoning_efforts.len() == 1; let preset_for_action = preset.clone(); let actions: Vec = vec![Box::new(move |tx| { @@ -2647,7 +2635,7 @@ impl ChatWidget { .or(Some(default_effort)); let model_slug = preset.model.to_string(); - let is_current_model = self.model_family.get_model_slug() == preset.model; + let is_current_model = self.model == preset.model; let highlight_choice = if is_current_model { self.config.model_reasoning_effort } else { @@ -3237,9 +3225,9 @@ impl ChatWidget { } /// Set the model in the widget's config copy. - pub(crate) fn set_model(&mut self, model: &str, model_family: ModelFamily) { + pub(crate) fn set_model(&mut self, model: &str) { self.session_header.set_model(model); - self.model_family = model_family; + self.model = model.to_string(); } pub(crate) fn add_info_message(&mut self, message: String, hint: Option) { diff --git a/codex-rs/tui/src/chatwidget/tests.rs b/codex-rs/tui/src/chatwidget/tests.rs index 5aa0e5d064e..c68eb111e34 100644 --- a/codex-rs/tui/src/chatwidget/tests.rs +++ b/codex-rs/tui/src/chatwidget/tests.rs @@ -313,7 +313,6 @@ async fn helpers_are_available_and_do_not_panic() { let tx = AppEventSender::new(tx_raw); let cfg = test_config().await; let resolved_model = ModelsManager::get_model_offline(cfg.model.as_deref()); - let model_family = ModelsManager::construct_model_family_offline(&resolved_model, &cfg); let conversation_manager = Arc::new(ConversationManager::with_models_provider( CodexAuth::from_api_key("test"), cfg.model_provider.clone(), @@ -330,7 +329,7 @@ async fn helpers_are_available_and_do_not_panic() { models_manager: conversation_manager.get_models_manager(), feedback: codex_feedback::CodexFeedback::new(), is_first_run: true, - model_family, + model: resolved_model, }; let mut w = ChatWidget::new(init, conversation_manager); // Basic construction sanity. @@ -371,11 +370,11 @@ async fn make_chatwidget_manual( codex_op_tx: op_tx, bottom_pane: bottom, active_cell: None, - config: cfg.clone(), - model_family: ModelsManager::construct_model_family_offline(&resolved_model, &cfg), + config: cfg, + model: resolved_model.clone(), auth_manager: auth_manager.clone(), models_manager: Arc::new(ModelsManager::new(auth_manager)), - session_header: SessionHeader::new(resolved_model.clone()), + session_header: SessionHeader::new(resolved_model), initial_user_message: None, token_info: None, rate_limit_snapshot: None, diff --git a/codex-rs/tui/src/status/card.rs b/codex-rs/tui/src/status/card.rs index 429134362a9..b8875b4c305 100644 --- a/codex-rs/tui/src/status/card.rs +++ b/codex-rs/tui/src/status/card.rs @@ -7,7 +7,6 @@ use chrono::DateTime; use chrono::Local; use codex_common::create_config_summary_entries; use codex_core::config::Config; -use codex_core::models_manager::model_family::ModelFamily; use codex_core::protocol::NetworkAccess; use codex_core::protocol::SandboxPolicy; use codex_core::protocol::TokenUsage; @@ -39,19 +38,11 @@ use crate::wrapping::RtOptions; use crate::wrapping::word_wrap_lines; use codex_core::AuthManager; -#[derive(Debug, Clone)] -struct StatusContextWindowData { - percent_remaining: i64, - tokens_in_context: i64, - window: i64, -} - #[derive(Debug, Clone)] pub(crate) struct StatusTokenUsageData { total: i64, input: i64, output: i64, - context_window: Option, } #[derive(Debug)] @@ -72,9 +63,7 @@ struct StatusHistoryCell { pub(crate) fn new_status_output( config: &Config, auth_manager: &AuthManager, - model_family: &ModelFamily, total_usage: &TokenUsage, - context_usage: Option<&TokenUsage>, session_id: &Option, rate_limits: Option<&RateLimitSnapshotDisplay>, plan_type: Option, @@ -85,9 +74,7 @@ pub(crate) fn new_status_output( let card = StatusHistoryCell::new( config, auth_manager, - model_family, total_usage, - context_usage, session_id, rate_limits, plan_type, @@ -103,9 +90,7 @@ impl StatusHistoryCell { fn new( config: &Config, auth_manager: &AuthManager, - model_family: &ModelFamily, total_usage: &TokenUsage, - context_usage: Option<&TokenUsage>, session_id: &Option, rate_limits: Option<&RateLimitSnapshotDisplay>, plan_type: Option, @@ -134,19 +119,11 @@ impl StatusHistoryCell { let agents_summary = compose_agents_summary(config); let account = compose_account_display(auth_manager, plan_type); let session_id = session_id.as_ref().map(std::string::ToString::to_string); - let context_window = model_family.context_window.and_then(|window| { - context_usage.map(|usage| StatusContextWindowData { - percent_remaining: usage.percent_of_context_window_remaining(window), - tokens_in_context: usage.tokens_in_context_window(), - window, - }) - }); let token_usage = StatusTokenUsageData { total: total_usage.blended_total(), input: total_usage.non_cached_input(), output: total_usage.output_tokens, - context_window, }; let rate_limits = compose_rate_limit_data(rate_limits, now); @@ -182,22 +159,6 @@ impl StatusHistoryCell { ] } - fn context_window_spans(&self) -> Option>> { - let context = self.token_usage.context_window.as_ref()?; - let percent = context.percent_remaining; - let used_fmt = format_tokens_compact(context.tokens_in_context); - let window_fmt = format_tokens_compact(context.window); - - Some(vec![ - Span::from(format!("{percent}% left")), - Span::from(" (").dim(), - Span::from(used_fmt).dim(), - Span::from(" used / ").dim(), - Span::from(window_fmt).dim(), - Span::from(")").dim(), - ]) - } - fn rate_limit_lines( &self, available_inner_width: usize, @@ -345,9 +306,7 @@ impl HistoryCell for StatusHistoryCell { push_label(&mut labels, &mut seen, "Session"); } push_label(&mut labels, &mut seen, "Token usage"); - if self.token_usage.context_window.is_some() { - push_label(&mut labels, &mut seen, "Context window"); - } + self.collect_rate_limit_labels(&mut seen, &mut labels); let formatter = FieldFormatter::from_labels(labels.iter().map(String::as_str)); @@ -399,10 +358,6 @@ impl HistoryCell for StatusHistoryCell { lines.push(formatter.line("Token usage", self.token_usage_spans())); } - if let Some(spans) = self.context_window_spans() { - lines.push(formatter.line("Context window", spans)); - } - lines.extend(self.rate_limit_lines(available_inner_width, &formatter)); let content_width = lines.iter().map(line_display_width).max().unwrap_or(0); diff --git a/codex-rs/tui/src/status/tests.rs b/codex-rs/tui/src/status/tests.rs index 317a3d32703..e1a134e74be 100644 --- a/codex-rs/tui/src/status/tests.rs +++ b/codex-rs/tui/src/status/tests.rs @@ -8,7 +8,6 @@ use codex_core::AuthManager; use codex_core::config::Config; use codex_core::config::ConfigBuilder; use codex_core::models_manager::manager::ModelsManager; -use codex_core::models_manager::model_family::ModelFamily; use codex_core::protocol::CreditsSnapshot; use codex_core::protocol::RateLimitSnapshot; use codex_core::protocol::RateLimitWindow; @@ -37,10 +36,6 @@ fn test_auth_manager(config: &Config) -> AuthManager { ) } -fn test_model_family(model_slug: &str, config: &Config) -> ModelFamily { - ModelsManager::construct_model_family_offline(model_slug, config) -} - fn render_lines(lines: &[Line<'static>]) -> Vec { lines .iter() @@ -132,14 +127,11 @@ async fn status_snapshot_includes_reasoning_details() { let rate_display = rate_limit_snapshot_display(&snapshot, captured_at); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); - let model_family = test_model_family(&model_slug, &config); let composite = new_status_output( &config, &auth_manager, - &model_family, &usage, - Some(&usage), &None, Some(&rate_display), None, @@ -190,13 +182,10 @@ async fn status_snapshot_includes_monthly_limit() { let rate_display = rate_limit_snapshot_display(&snapshot, captured_at); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); - let model_family = test_model_family(&model_slug, &config); let composite = new_status_output( &config, &auth_manager, - &model_family, &usage, - Some(&usage), &None, Some(&rate_display), None, @@ -235,13 +224,10 @@ async fn status_snapshot_shows_unlimited_credits() { }; let rate_display = rate_limit_snapshot_display(&snapshot, captured_at); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); - let model_family = test_model_family(&model_slug, &config); let composite = new_status_output( &config, &auth_manager, - &model_family, &usage, - Some(&usage), &None, Some(&rate_display), None, @@ -279,13 +265,10 @@ async fn status_snapshot_shows_positive_credits() { }; let rate_display = rate_limit_snapshot_display(&snapshot, captured_at); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); - let model_family = test_model_family(&model_slug, &config); let composite = new_status_output( &config, &auth_manager, - &model_family, &usage, - Some(&usage), &None, Some(&rate_display), None, @@ -323,13 +306,10 @@ async fn status_snapshot_hides_zero_credits() { }; let rate_display = rate_limit_snapshot_display(&snapshot, captured_at); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); - let model_family = test_model_family(&model_slug, &config); let composite = new_status_output( &config, &auth_manager, - &model_family, &usage, - Some(&usage), &None, Some(&rate_display), None, @@ -365,13 +345,10 @@ async fn status_snapshot_hides_when_has_no_credits_flag() { }; let rate_display = rate_limit_snapshot_display(&snapshot, captured_at); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); - let model_family = test_model_family(&model_slug, &config); let composite = new_status_output( &config, &auth_manager, - &model_family, &usage, - Some(&usage), &None, Some(&rate_display), None, @@ -407,13 +384,10 @@ async fn status_card_token_usage_excludes_cached_tokens() { .expect("timestamp"); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); - let model_family = test_model_family(&model_slug, &config); let composite = new_status_output( &config, &auth_manager, - &model_family, &usage, - Some(&usage), &None, None, None, @@ -464,13 +438,10 @@ async fn status_snapshot_truncates_in_narrow_terminal() { let rate_display = rate_limit_snapshot_display(&snapshot, captured_at); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); - let model_family = test_model_family(&model_slug, &config); let composite = new_status_output( &config, &auth_manager, - &model_family, &usage, - Some(&usage), &None, Some(&rate_display), None, @@ -510,13 +481,10 @@ async fn status_snapshot_shows_missing_limits_message() { .expect("timestamp"); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); - let model_family = test_model_family(&model_slug, &config); let composite = new_status_output( &config, &auth_manager, - &model_family, &usage, - Some(&usage), &None, None, None, @@ -574,13 +542,10 @@ async fn status_snapshot_includes_credits_and_limits() { let rate_display = rate_limit_snapshot_display(&snapshot, captured_at); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); - let model_family = test_model_family(&model_slug, &config); let composite = new_status_output( &config, &auth_manager, - &model_family, &usage, - Some(&usage), &None, Some(&rate_display), None, @@ -626,13 +591,10 @@ async fn status_snapshot_shows_empty_limits_message() { let rate_display = rate_limit_snapshot_display(&snapshot, captured_at); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); - let model_family = test_model_family(&model_slug, &config); let composite = new_status_output( &config, &auth_manager, - &model_family, &usage, - Some(&usage), &None, Some(&rate_display), None, @@ -687,13 +649,10 @@ async fn status_snapshot_shows_stale_limits_message() { let now = captured_at + ChronoDuration::minutes(20); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); - let model_family = test_model_family(&model_slug, &config); let composite = new_status_output( &config, &auth_manager, - &model_family, &usage, - Some(&usage), &None, Some(&rate_display), None, @@ -752,13 +711,10 @@ async fn status_snapshot_cached_limits_hide_credits_without_flag() { let now = captured_at + ChronoDuration::minutes(20); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); - let model_family = test_model_family(&model_slug, &config); let composite = new_status_output( &config, &auth_manager, - &model_family, &usage, - Some(&usage), &None, Some(&rate_display), None, @@ -789,13 +745,6 @@ async fn status_context_window_uses_last_usage() { reasoning_output_tokens: 0, total_tokens: 102_000, }; - let last_usage = TokenUsage { - input_tokens: 12_800, - cached_input_tokens: 0, - output_tokens: 879, - reasoning_output_tokens: 0, - total_tokens: 13_679, - }; let now = chrono::Local .with_ymd_and_hms(2024, 6, 1, 12, 0, 0) @@ -803,13 +752,10 @@ async fn status_context_window_uses_last_usage() { .expect("timestamp"); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); - let model_family = test_model_family(&model_slug, &config); let composite = new_status_output( &config, &auth_manager, - &model_family, &total_usage, - Some(&last_usage), &None, None, None, From a856ae41cc8b937940134955c37bedca1e57c7c2 Mon Sep 17 00:00:00 2001 From: Ahmed Ibrahim Date: Tue, 23 Dec 2025 13:29:20 -0800 Subject: [PATCH 3/8] remove-model-family-2 --- codex-rs/codex-api/src/endpoint/models.rs | 1 - codex-rs/core/src/models_manager/manager.rs | 1 - docs/config.md | 1 - docs/example-config.md | 3 --- 4 files changed, 6 deletions(-) diff --git a/codex-rs/codex-api/src/endpoint/models.rs b/codex-rs/codex-api/src/endpoint/models.rs index b15f07fca2a..cb43f4ff5b2 100644 --- a/codex-rs/codex-api/src/endpoint/models.rs +++ b/codex-rs/codex-api/src/endpoint/models.rs @@ -227,7 +227,6 @@ mod tests { "truncation_policy": {"mode": "bytes", "limit": 10_000}, "supports_parallel_tool_calls": false, "context_window": null, - "reasoning_summary_format": "none", "experimental_supported_tools": [], })) .unwrap(), diff --git a/codex-rs/core/src/models_manager/manager.rs b/codex-rs/core/src/models_manager/manager.rs index 315380ade10..5c53f5fc166 100644 --- a/codex-rs/core/src/models_manager/manager.rs +++ b/codex-rs/core/src/models_manager/manager.rs @@ -354,7 +354,6 @@ mod tests { "truncation_policy": {"mode": "bytes", "limit": 10_000}, "supports_parallel_tool_calls": false, "context_window": null, - "reasoning_summary_format": "none", "experimental_supported_tools": [], })) .expect("valid model") diff --git a/docs/config.md b/docs/config.md index 5198b22266d..8452a831e25 100644 --- a/docs/config.md +++ b/docs/config.md @@ -1060,7 +1060,6 @@ Valid values: | `model_reasoning_summary` | `auto` \| `concise` \| `detailed` \| `none` | Reasoning summaries. | | `model_verbosity` | `low` \| `medium` \| `high` | GPT‑5 text verbosity (Responses API). | | `model_supports_reasoning_summaries` | boolean | Force‑enable reasoning summaries. | -| `model_reasoning_summary_format` | `none` \| `experimental` | Force reasoning summary format. | | `chatgpt_base_url` | string | Base URL for ChatGPT auth flow. | | `experimental_instructions_file` | string (path) | Replace built‑in instructions (experimental). | | `experimental_use_exec_command_tool` | boolean | Use experimental exec command tool. | diff --git a/docs/example-config.md b/docs/example-config.md index bfc467baf87..795b2903904 100644 --- a/docs/example-config.md +++ b/docs/example-config.md @@ -49,9 +49,6 @@ model_verbosity = "medium" # Force-enable reasoning summaries for current model (default: false) model_supports_reasoning_summaries = false -# Force reasoning summary format: none | experimental (default: none) -model_reasoning_summary_format = "none" - ################################################################################ # Instruction Overrides ################################################################################ From bebab9fc5f6a50394c954a739da7b9048bf376e1 Mon Sep 17 00:00:00 2001 From: Ahmed Ibrahim Date: Tue, 23 Dec 2025 16:17:17 -0800 Subject: [PATCH 4/8] fix --- ...ched_limits_hide_credits_without_flag.snap | 35 +++++++++---------- ..._snapshot_includes_credits_and_limits.snap | 35 +++++++++---------- ...tatus_snapshot_includes_monthly_limit.snap | 31 ++++++++-------- ...s_snapshot_includes_reasoning_details.snap | 33 +++++++++-------- ...s_snapshot_shows_empty_limits_message.snap | 31 ++++++++-------- ...snapshot_shows_missing_limits_message.snap | 31 ++++++++-------- ...s_snapshot_shows_stale_limits_message.snap | 35 +++++++++---------- ...snapshot_truncates_in_narrow_terminal.snap | 13 ++++--- 8 files changed, 118 insertions(+), 126 deletions(-) diff --git a/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_cached_limits_hide_credits_without_flag.snap b/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_cached_limits_hide_credits_without_flag.snap index dbb634bab1c..949b034516f 100644 --- a/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_cached_limits_hide_credits_without_flag.snap +++ b/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_cached_limits_hide_credits_without_flag.snap @@ -4,21 +4,20 @@ expression: sanitized --- /status -╭─────────────────────────────────────────────────────────────────────╮ -│ >_ OpenAI Codex (v0.0.0) │ -│ │ -│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ -│ information on rate limits and credits │ -│ │ -│ Model: gpt-5.1-codex (reasoning none, summaries auto) │ -│ Directory: [[workspace]] │ -│ Approval: on-request │ -│ Sandbox: read-only │ -│ Agents.md: │ -│ │ -│ Token usage: 1.05K total (700 input + 350 output) │ -│ Context window: 100% left (1.45K used / 272K) │ -│ 5h limit: [████████░░░░░░░░░░░░] 40% left (resets 11:32) │ -│ Weekly limit: [█████████████░░░░░░░] 65% left (resets 11:52) │ -│ Warning: limits may be stale - start new turn to refresh. │ -╰─────────────────────────────────────────────────────────────────────╯ +╭───────────────────────────────────────────────────────────────────╮ +│ >_ OpenAI Codex (v0.0.0) │ +│ │ +│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ +│ information on rate limits and credits │ +│ │ +│ Model: gpt-5.1-codex (reasoning none, summaries auto) │ +│ Directory: [[workspace]] │ +│ Approval: on-request │ +│ Sandbox: read-only │ +│ Agents.md: │ +│ │ +│ Token usage: 1.05K total (700 input + 350 output) │ +│ 5h limit: [████████░░░░░░░░░░░░] 40% left (resets 11:32) │ +│ Weekly limit: [█████████████░░░░░░░] 65% left (resets 11:52) │ +│ Warning: limits may be stale - start new turn to refresh. │ +╰───────────────────────────────────────────────────────────────────╯ diff --git a/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_includes_credits_and_limits.snap b/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_includes_credits_and_limits.snap index 1707a4c5fbc..9afa9f8cf9f 100644 --- a/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_includes_credits_and_limits.snap +++ b/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_includes_credits_and_limits.snap @@ -4,21 +4,20 @@ expression: sanitized --- /status -╭───────────────────────────────────────────────────────────────────╮ -│ >_ OpenAI Codex (v0.0.0) │ -│ │ -│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ -│ information on rate limits and credits │ -│ │ -│ Model: gpt-5.1-codex (reasoning none, summaries auto) │ -│ Directory: [[workspace]] │ -│ Approval: on-request │ -│ Sandbox: read-only │ -│ Agents.md: │ -│ │ -│ Token usage: 2K total (1.4K input + 600 output) │ -│ Context window: 100% left (2.2K used / 272K) │ -│ 5h limit: [███████████░░░░░░░░░] 55% left (resets 09:25) │ -│ Weekly limit: [██████████████░░░░░░] 70% left (resets 09:55) │ -│ Credits: 38 credits │ -╰───────────────────────────────────────────────────────────────────╯ +╭─────────────────────────────────────────────────────────────────╮ +│ >_ OpenAI Codex (v0.0.0) │ +│ │ +│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ +│ information on rate limits and credits │ +│ │ +│ Model: gpt-5.1-codex (reasoning none, summaries auto) │ +│ Directory: [[workspace]] │ +│ Approval: on-request │ +│ Sandbox: read-only │ +│ Agents.md: │ +│ │ +│ Token usage: 2K total (1.4K input + 600 output) │ +│ 5h limit: [███████████░░░░░░░░░] 55% left (resets 09:25) │ +│ Weekly limit: [██████████████░░░░░░] 70% left (resets 09:55) │ +│ Credits: 38 credits │ +╰─────────────────────────────────────────────────────────────────╯ diff --git a/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_includes_monthly_limit.snap b/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_includes_monthly_limit.snap index 3ecc4fa8ed2..2d35bb69042 100644 --- a/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_includes_monthly_limit.snap +++ b/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_includes_monthly_limit.snap @@ -4,19 +4,18 @@ expression: sanitized --- /status -╭────────────────────────────────────────────────────────────────────────────╮ -│ >_ OpenAI Codex (v0.0.0) │ -│ │ -│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ -│ information on rate limits and credits │ -│ │ -│ Model: gpt-5.1-codex-max (reasoning none, summaries auto) │ -│ Directory: [[workspace]] │ -│ Approval: on-request │ -│ Sandbox: read-only │ -│ Agents.md: │ -│ │ -│ Token usage: 1.2K total (800 input + 400 output) │ -│ Context window: 100% left (1.2K used / 272K) │ -│ Monthly limit: [██████████████████░░] 88% left (resets 07:08 on 7 May) │ -╰────────────────────────────────────────────────────────────────────────────╯ +╭───────────────────────────────────────────────────────────────────────────╮ +│ >_ OpenAI Codex (v0.0.0) │ +│ │ +│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ +│ information on rate limits and credits │ +│ │ +│ Model: gpt-5.1-codex-max (reasoning none, summaries auto) │ +│ Directory: [[workspace]] │ +│ Approval: on-request │ +│ Sandbox: read-only │ +│ Agents.md: │ +│ │ +│ Token usage: 1.2K total (800 input + 400 output) │ +│ Monthly limit: [██████████████████░░] 88% left (resets 07:08 on 7 May) │ +╰───────────────────────────────────────────────────────────────────────────╯ diff --git a/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_includes_reasoning_details.snap b/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_includes_reasoning_details.snap index c22577407ee..604b463128d 100644 --- a/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_includes_reasoning_details.snap +++ b/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_includes_reasoning_details.snap @@ -4,20 +4,19 @@ expression: sanitized --- /status -╭───────────────────────────────────────────────────────────────────────────╮ -│ >_ OpenAI Codex (v0.0.0) │ -│ │ -│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ -│ information on rate limits and credits │ -│ │ -│ Model: gpt-5.1-codex-max (reasoning high, summaries detailed) │ -│ Directory: [[workspace]] │ -│ Approval: on-request │ -│ Sandbox: workspace-write │ -│ Agents.md: │ -│ │ -│ Token usage: 1.9K total (1K input + 900 output) │ -│ Context window: 100% left (2.25K used / 272K) │ -│ 5h limit: [██████░░░░░░░░░░░░░░] 28% left (resets 03:14) │ -│ Weekly limit: [███████████░░░░░░░░░] 55% left (resets 03:24) │ -╰───────────────────────────────────────────────────────────────────────────╯ +╭─────────────────────────────────────────────────────────────────────────╮ +│ >_ OpenAI Codex (v0.0.0) │ +│ │ +│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ +│ information on rate limits and credits │ +│ │ +│ Model: gpt-5.1-codex-max (reasoning high, summaries detailed) │ +│ Directory: [[workspace]] │ +│ Approval: on-request │ +│ Sandbox: workspace-write │ +│ Agents.md: │ +│ │ +│ Token usage: 1.9K total (1K input + 900 output) │ +│ 5h limit: [██████░░░░░░░░░░░░░░] 28% left (resets 03:14) │ +│ Weekly limit: [███████████░░░░░░░░░] 55% left (resets 03:24) │ +╰─────────────────────────────────────────────────────────────────────────╯ diff --git a/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_shows_empty_limits_message.snap b/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_shows_empty_limits_message.snap index f0e6b734454..ce58f59a288 100644 --- a/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_shows_empty_limits_message.snap +++ b/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_shows_empty_limits_message.snap @@ -4,19 +4,18 @@ expression: sanitized --- /status -╭───────────────────────────────────────────────────────────────────────╮ -│ >_ OpenAI Codex (v0.0.0) │ -│ │ -│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ -│ information on rate limits and credits │ -│ │ -│ Model: gpt-5.1-codex-max (reasoning none, summaries auto) │ -│ Directory: [[workspace]] │ -│ Approval: on-request │ -│ Sandbox: read-only │ -│ Agents.md: │ -│ │ -│ Token usage: 750 total (500 input + 250 output) │ -│ Context window: 100% left (750 used / 272K) │ -│ Limits: data not available yet │ -╰───────────────────────────────────────────────────────────────────────╯ +╭────────────────────────────────────────────────────────────────────╮ +│ >_ OpenAI Codex (v0.0.0) │ +│ │ +│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ +│ information on rate limits and credits │ +│ │ +│ Model: gpt-5.1-codex-max (reasoning none, summaries auto) │ +│ Directory: [[workspace]] │ +│ Approval: on-request │ +│ Sandbox: read-only │ +│ Agents.md: │ +│ │ +│ Token usage: 750 total (500 input + 250 output) │ +│ Limits: data not available yet │ +╰────────────────────────────────────────────────────────────────────╯ diff --git a/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_shows_missing_limits_message.snap b/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_shows_missing_limits_message.snap index f0e6b734454..ce58f59a288 100644 --- a/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_shows_missing_limits_message.snap +++ b/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_shows_missing_limits_message.snap @@ -4,19 +4,18 @@ expression: sanitized --- /status -╭───────────────────────────────────────────────────────────────────────╮ -│ >_ OpenAI Codex (v0.0.0) │ -│ │ -│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ -│ information on rate limits and credits │ -│ │ -│ Model: gpt-5.1-codex-max (reasoning none, summaries auto) │ -│ Directory: [[workspace]] │ -│ Approval: on-request │ -│ Sandbox: read-only │ -│ Agents.md: │ -│ │ -│ Token usage: 750 total (500 input + 250 output) │ -│ Context window: 100% left (750 used / 272K) │ -│ Limits: data not available yet │ -╰───────────────────────────────────────────────────────────────────────╯ +╭────────────────────────────────────────────────────────────────────╮ +│ >_ OpenAI Codex (v0.0.0) │ +│ │ +│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ +│ information on rate limits and credits │ +│ │ +│ Model: gpt-5.1-codex-max (reasoning none, summaries auto) │ +│ Directory: [[workspace]] │ +│ Approval: on-request │ +│ Sandbox: read-only │ +│ Agents.md: │ +│ │ +│ Token usage: 750 total (500 input + 250 output) │ +│ Limits: data not available yet │ +╰────────────────────────────────────────────────────────────────────╯ diff --git a/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_shows_stale_limits_message.snap b/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_shows_stale_limits_message.snap index a12be950bcc..2c20713a322 100644 --- a/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_shows_stale_limits_message.snap +++ b/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_shows_stale_limits_message.snap @@ -4,21 +4,20 @@ expression: sanitized --- /status -╭───────────────────────────────────────────────────────────────────────╮ -│ >_ OpenAI Codex (v0.0.0) │ -│ │ -│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ -│ information on rate limits and credits │ -│ │ -│ Model: gpt-5.1-codex-max (reasoning none, summaries auto) │ -│ Directory: [[workspace]] │ -│ Approval: on-request │ -│ Sandbox: read-only │ -│ Agents.md: │ -│ │ -│ Token usage: 1.9K total (1K input + 900 output) │ -│ Context window: 100% left (2.25K used / 272K) │ -│ 5h limit: [██████░░░░░░░░░░░░░░] 28% left (resets 03:14) │ -│ Weekly limit: [████████████░░░░░░░░] 60% left (resets 03:34) │ -│ Warning: limits may be stale - start new turn to refresh. │ -╰───────────────────────────────────────────────────────────────────────╯ +╭─────────────────────────────────────────────────────────────────────╮ +│ >_ OpenAI Codex (v0.0.0) │ +│ │ +│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ +│ information on rate limits and credits │ +│ │ +│ Model: gpt-5.1-codex-max (reasoning none, summaries auto) │ +│ Directory: [[workspace]] │ +│ Approval: on-request │ +│ Sandbox: read-only │ +│ Agents.md: │ +│ │ +│ Token usage: 1.9K total (1K input + 900 output) │ +│ 5h limit: [██████░░░░░░░░░░░░░░] 28% left (resets 03:14) │ +│ Weekly limit: [████████████░░░░░░░░] 60% left (resets 03:34) │ +│ Warning: limits may be stale - start new turn to refresh. │ +╰─────────────────────────────────────────────────────────────────────╯ diff --git a/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_truncates_in_narrow_terminal.snap b/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_truncates_in_narrow_terminal.snap index 02ba1adec91..2d43cda3eca 100644 --- a/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_truncates_in_narrow_terminal.snap +++ b/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_truncates_in_narrow_terminal.snap @@ -10,13 +10,12 @@ expression: sanitized │ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ │ information on rate limits and credits │ │ │ -│ Model: gpt-5.1-codex-max (reasoning high, summaries de │ +│ Model: gpt-5.1-codex-max (reasoning high, summaries detai │ │ Directory: [[workspace]] │ -│ Approval: on-request │ -│ Sandbox: read-only │ -│ Agents.md: │ +│ Approval: on-request │ +│ Sandbox: read-only │ +│ Agents.md: │ │ │ -│ Token usage: 1.9K total (1K input + 900 output) │ -│ Context window: 100% left (2.25K used / 272K) │ -│ 5h limit: [██████░░░░░░░░░░░░░░] 28% left (resets 03:14) │ +│ Token usage: 1.9K total (1K input + 900 output) │ +│ 5h limit: [██████░░░░░░░░░░░░░░] 28% left (resets 03:14) │ ╰────────────────────────────────────────────────────────────────────╯ From c2f7981fdb8524baefcee5b296aa6f94cfcaec79 Mon Sep 17 00:00:00 2001 From: Ahmed Ibrahim Date: Tue, 23 Dec 2025 16:52:46 -0800 Subject: [PATCH 5/8] remove-model-family-2 --- codex-rs/tui2/src/app.rs | 30 ++--- codex-rs/tui2/src/app_backtrack.rs | 4 +- codex-rs/tui2/src/chatwidget.rs | 62 +++++------ codex-rs/tui2/src/chatwidget/tests.rs | 9 +- codex-rs/tui2/src/status/card.rs | 46 -------- ...ched_limits_hide_credits_without_flag.snap | 35 +++--- ..._snapshot_includes_credits_and_limits.snap | 35 +++--- ...tatus_snapshot_includes_monthly_limit.snap | 31 +++--- ...s_snapshot_includes_reasoning_details.snap | 33 +++--- ...s_snapshot_shows_empty_limits_message.snap | 31 +++--- ...snapshot_shows_missing_limits_message.snap | 31 +++--- ...s_snapshot_shows_stale_limits_message.snap | 35 +++--- ...snapshot_truncates_in_narrow_terminal.snap | 13 +-- codex-rs/tui2/src/status/tests.rs | 104 ------------------ 14 files changed, 155 insertions(+), 344 deletions(-) diff --git a/codex-rs/tui2/src/app.rs b/codex-rs/tui2/src/app.rs index 78ccfb03871..135e45d491d 100644 --- a/codex-rs/tui2/src/app.rs +++ b/codex-rs/tui2/src/app.rs @@ -410,10 +410,6 @@ impl App { } let enhanced_keys_supported = tui.enhanced_keys_supported(); - let model_family = conversation_manager - .get_models_manager() - .construct_model_family(model.as_str(), &config) - .await; let mut chat_widget = match resume_selection { ResumeSelection::StartFresh | ResumeSelection::Exit => { let init = crate::chatwidget::ChatWidgetInit { @@ -427,7 +423,7 @@ impl App { models_manager: conversation_manager.get_models_manager(), feedback: feedback.clone(), is_first_run, - model_family: model_family.clone(), + model: model.clone(), }; ChatWidget::new(init, conversation_manager.clone()) } @@ -453,7 +449,7 @@ impl App { models_manager: conversation_manager.get_models_manager(), feedback: feedback.clone(), is_first_run, - model_family: model_family.clone(), + model: model.clone(), }; ChatWidget::new_from_existing( init, @@ -1329,11 +1325,6 @@ impl App { } async fn handle_event(&mut self, tui: &mut tui::Tui, event: AppEvent) -> Result { - let model_family = self - .server - .get_models_manager() - .construct_model_family(self.current_model.as_str(), &self.config) - .await; match event { AppEvent::NewSession => { let summary = session_summary( @@ -1352,10 +1343,9 @@ impl App { models_manager: self.server.get_models_manager(), feedback: self.feedback.clone(), is_first_run: false, - model_family: model_family.clone(), + model: self.current_model.clone(), }; self.chat_widget = ChatWidget::new(init, self.server.clone()); - self.current_model = model_family.get_model_slug().to_string(); if let Some(summary) = summary { let mut lines: Vec> = vec![summary.usage_line.clone().into()]; if let Some(command) = summary.resume_command { @@ -1402,14 +1392,13 @@ impl App { models_manager: self.server.get_models_manager(), feedback: self.feedback.clone(), is_first_run: false, - model_family: model_family.clone(), + model: self.current_model.clone(), }; self.chat_widget = ChatWidget::new_from_existing( init, resumed.conversation, resumed.session_configured, ); - self.current_model = model_family.get_model_slug().to_string(); if let Some(summary) = summary { let mut lines: Vec> = vec![summary.usage_line.clone().into()]; @@ -1535,12 +1524,7 @@ impl App { self.on_update_reasoning_effort(effort); } AppEvent::UpdateModel(model) => { - let model_family = self - .server - .get_models_manager() - .construct_model_family(&model, &self.config) - .await; - self.chat_widget.set_model(&model, model_family); + self.chat_widget.set_model(&model); self.current_model = model; } AppEvent::OpenReasoningPopup { model } => { @@ -2088,7 +2072,7 @@ mod tests { async fn make_test_app() -> App { let (chat_widget, app_event_tx, _rx, _op_rx) = make_chatwidget_manual_with_sender().await; let config = chat_widget.config_ref().clone(); - let current_model = chat_widget.get_model_family().get_model_slug().to_string(); + let current_model = "gpt-5.2-codex".to_string(); let server = Arc::new(ConversationManager::with_models_provider( CodexAuth::from_api_key("Test API Key"), config.model_provider.clone(), @@ -2137,7 +2121,7 @@ mod tests { ) { let (chat_widget, app_event_tx, rx, op_rx) = make_chatwidget_manual_with_sender().await; let config = chat_widget.config_ref().clone(); - let current_model = chat_widget.get_model_family().get_model_slug().to_string(); + let current_model = "gpt-5.2-codex".to_string(); let server = Arc::new(ConversationManager::with_models_provider( CodexAuth::from_api_key("Test API Key"), config.model_provider.clone(), diff --git a/codex-rs/tui2/src/app_backtrack.rs b/codex-rs/tui2/src/app_backtrack.rs index 671702d3082..ce5dff2ed85 100644 --- a/codex-rs/tui2/src/app_backtrack.rs +++ b/codex-rs/tui2/src/app_backtrack.rs @@ -338,10 +338,9 @@ impl App { ) { let conv = new_conv.conversation; let session_configured = new_conv.session_configured; - let model_family = self.chat_widget.get_model_family(); let init = crate::chatwidget::ChatWidgetInit { config: cfg, - model_family: model_family.clone(), + model: self.current_model.clone(), frame_requester: tui.frame_requester(), app_event_tx: self.app_event_tx.clone(), initial_prompt: None, @@ -354,7 +353,6 @@ impl App { }; self.chat_widget = crate::chatwidget::ChatWidget::new_from_existing(init, conv, session_configured); - self.current_model = model_family.get_model_slug().to_string(); // Trim transcript up to the selected user message and re-render it. self.trim_transcript_for_backtrack(nth_user_message); self.render_transcript_once(tui); diff --git a/codex-rs/tui2/src/chatwidget.rs b/codex-rs/tui2/src/chatwidget.rs index 723390ef8b0..7a1e601c718 100644 --- a/codex-rs/tui2/src/chatwidget.rs +++ b/codex-rs/tui2/src/chatwidget.rs @@ -13,7 +13,6 @@ use codex_core::config::types::Notifications; use codex_core::git_info::current_branch_name; use codex_core::git_info::local_git_branches; use codex_core::models_manager::manager::ModelsManager; -use codex_core::models_manager::model_family::ModelFamily; use codex_core::project_doc::DEFAULT_PROJECT_DOC_FILENAME; use codex_core::protocol::AgentMessageDeltaEvent; use codex_core::protocol::AgentMessageEvent; @@ -267,7 +266,7 @@ pub(crate) struct ChatWidgetInit { pub(crate) models_manager: Arc, pub(crate) feedback: codex_feedback::CodexFeedback, pub(crate) is_first_run: bool, - pub(crate) model_family: ModelFamily, + pub(crate) model: String, } #[derive(Default)] @@ -284,7 +283,7 @@ pub(crate) struct ChatWidget { bottom_pane: BottomPane, active_cell: Option>, config: Config, - model_family: ModelFamily, + model: String, auth_manager: Arc, models_manager: Arc, session_header: SessionHeader, @@ -573,12 +572,10 @@ impl ChatWidget { } fn context_remaining_percent(&self, info: &TokenUsageInfo) -> Option { - info.model_context_window - .or(self.model_family.context_window) - .map(|window| { - info.last_token_usage - .percent_of_context_window_remaining(window) - }) + info.model_context_window.map(|window| { + info.last_token_usage + .percent_of_context_window_remaining(window) + }) } fn context_used_tokens(&self, info: &TokenUsageInfo, percent_known: bool) -> Option { @@ -646,7 +643,7 @@ impl ChatWidget { if high_usage && !self.rate_limit_switch_prompt_hidden() - && self.model_family.get_model_slug() != NUDGE_MODEL_SLUG + && self.model != NUDGE_MODEL_SLUG && !matches!( self.rate_limit_switch_prompt, RateLimitSwitchPromptState::Shown @@ -680,9 +677,6 @@ impl ChatWidget { self.stream_controller = None; self.maybe_show_pending_rate_limit_prompt(); } - pub(crate) fn get_model_family(&self) -> ModelFamily { - self.model_family.clone() - } fn on_error(&mut self, message: String) { self.finalize_turn(); @@ -1280,11 +1274,10 @@ impl ChatWidget { models_manager, feedback, is_first_run, - model_family, + model, } = common; - let model_slug = model_family.get_model_slug().to_string(); let mut config = config; - config.model = Some(model_slug.clone()); + config.model = Some(model.clone()); let mut rng = rand::rng(); let placeholder = EXAMPLE_PROMPTS[rng.random_range(0..EXAMPLE_PROMPTS.len())].to_string(); let codex_op_tx = spawn_agent(config.clone(), app_event_tx.clone(), conversation_manager); @@ -1305,10 +1298,10 @@ impl ChatWidget { }), active_cell: None, config, - model_family, + model: model.clone(), auth_manager, models_manager, - session_header: SessionHeader::new(model_slug), + session_header: SessionHeader::new(model), initial_user_message: create_initial_user_message( initial_prompt.unwrap_or_default(), initial_images, @@ -1364,10 +1357,9 @@ impl ChatWidget { auth_manager, models_manager, feedback, - model_family, + model, .. } = common; - let model_slug = model_family.get_model_slug().to_string(); let mut rng = rand::rng(); let placeholder = EXAMPLE_PROMPTS[rng.random_range(0..EXAMPLE_PROMPTS.len())].to_string(); @@ -1390,10 +1382,10 @@ impl ChatWidget { }), active_cell: None, config, - model_family, + model: model.clone(), auth_manager, models_manager, - session_header: SessionHeader::new(model_slug), + session_header: SessionHeader::new(model), initial_user_message: create_initial_user_message( initial_prompt.unwrap_or_default(), initial_images, @@ -2050,22 +2042,20 @@ impl ChatWidget { pub(crate) fn add_status_output(&mut self) { let default_usage = TokenUsage::default(); - let (total_usage, context_usage) = if let Some(ti) = &self.token_info { - (&ti.total_token_usage, Some(&ti.last_token_usage)) + let total_usage = if let Some(ti) = &self.token_info { + &ti.total_token_usage } else { - (&default_usage, Some(&default_usage)) + &default_usage }; self.add_to_history(crate::status::new_status_output( &self.config, self.auth_manager.as_ref(), - &self.model_family, total_usage, - context_usage, &self.conversation_id, self.rate_limit_snapshot.as_ref(), self.plan_type, Local::now(), - self.model_family.get_model_slug(), + &self.model, )); } fn stop_rate_limit_poller(&mut self) { @@ -2208,7 +2198,6 @@ impl ChatWidget { /// Open a popup to choose a quick auto model. Selecting "All models" /// opens the full picker with every available preset. pub(crate) fn open_model_popup(&mut self) { - let current_model = self.model_family.get_model_slug().to_string(); let presets: Vec = // todo(aibrahim): make this async function match self.models_manager.try_list_models(&self.config) { @@ -2225,9 +2214,9 @@ impl ChatWidget { let current_label = presets .iter() - .find(|preset| preset.model == current_model) + .find(|preset| preset.model == self.model) .map(|preset| preset.display_name.to_string()) - .unwrap_or_else(|| current_model.clone()); + .unwrap_or_else(|| self.model.clone()); let (mut auto_presets, other_presets): (Vec, Vec) = presets .into_iter() @@ -2253,7 +2242,7 @@ impl ChatWidget { SelectionItem { name: preset.display_name.clone(), description, - is_current: model == current_model, + is_current: model == self.model, is_default: preset.is_default, actions, dismiss_on_select: true, @@ -2316,12 +2305,11 @@ impl ChatWidget { return; } - let current_model = self.model_family.get_model_slug().to_string(); let mut items: Vec = Vec::new(); for preset in presets.into_iter() { let description = (!preset.description.is_empty()).then_some(preset.description.to_string()); - let is_current = preset.model == current_model; + let is_current = preset.model == self.model; let single_supported_effort = preset.supported_reasoning_efforts.len() == 1; let preset_for_action = preset.clone(); let actions: Vec = vec![Box::new(move |tx| { @@ -2447,7 +2435,7 @@ impl ChatWidget { .or(Some(default_effort)); let model_slug = preset.model.to_string(); - let is_current_model = self.model_family.get_model_slug() == preset.model; + let is_current_model = self.model == preset.model; let highlight_choice = if is_current_model { self.config.model_reasoning_effort } else { @@ -3006,9 +2994,9 @@ impl ChatWidget { } /// Set the model in the widget's config copy. - pub(crate) fn set_model(&mut self, model: &str, model_family: ModelFamily) { + pub(crate) fn set_model(&mut self, model: &str) { self.session_header.set_model(model); - self.model_family = model_family; + self.model = model.to_string(); } pub(crate) fn add_info_message(&mut self, message: String, hint: Option) { diff --git a/codex-rs/tui2/src/chatwidget/tests.rs b/codex-rs/tui2/src/chatwidget/tests.rs index a71be3a635b..cc4f6843c00 100644 --- a/codex-rs/tui2/src/chatwidget/tests.rs +++ b/codex-rs/tui2/src/chatwidget/tests.rs @@ -311,7 +311,6 @@ async fn helpers_are_available_and_do_not_panic() { let tx = AppEventSender::new(tx_raw); let cfg = test_config().await; let resolved_model = ModelsManager::get_model_offline(cfg.model.as_deref()); - let model_family = ModelsManager::construct_model_family_offline(&resolved_model, &cfg); let conversation_manager = Arc::new(ConversationManager::with_models_provider( CodexAuth::from_api_key("test"), cfg.model_provider.clone(), @@ -328,7 +327,7 @@ async fn helpers_are_available_and_do_not_panic() { models_manager: conversation_manager.get_models_manager(), feedback: codex_feedback::CodexFeedback::new(), is_first_run: true, - model_family, + model: resolved_model, }; let mut w = ChatWidget::new(init, conversation_manager); // Basic construction sanity. @@ -369,11 +368,11 @@ async fn make_chatwidget_manual( codex_op_tx: op_tx, bottom_pane: bottom, active_cell: None, - config: cfg.clone(), - model_family: ModelsManager::construct_model_family_offline(&resolved_model, &cfg), + config: cfg, + model: resolved_model.clone(), auth_manager: auth_manager.clone(), models_manager: Arc::new(ModelsManager::new(auth_manager)), - session_header: SessionHeader::new(resolved_model.clone()), + session_header: SessionHeader::new(resolved_model), initial_user_message: None, token_info: None, rate_limit_snapshot: None, diff --git a/codex-rs/tui2/src/status/card.rs b/codex-rs/tui2/src/status/card.rs index 429134362a9..e795647cf5f 100644 --- a/codex-rs/tui2/src/status/card.rs +++ b/codex-rs/tui2/src/status/card.rs @@ -7,7 +7,6 @@ use chrono::DateTime; use chrono::Local; use codex_common::create_config_summary_entries; use codex_core::config::Config; -use codex_core::models_manager::model_family::ModelFamily; use codex_core::protocol::NetworkAccess; use codex_core::protocol::SandboxPolicy; use codex_core::protocol::TokenUsage; @@ -39,19 +38,11 @@ use crate::wrapping::RtOptions; use crate::wrapping::word_wrap_lines; use codex_core::AuthManager; -#[derive(Debug, Clone)] -struct StatusContextWindowData { - percent_remaining: i64, - tokens_in_context: i64, - window: i64, -} - #[derive(Debug, Clone)] pub(crate) struct StatusTokenUsageData { total: i64, input: i64, output: i64, - context_window: Option, } #[derive(Debug)] @@ -72,9 +63,7 @@ struct StatusHistoryCell { pub(crate) fn new_status_output( config: &Config, auth_manager: &AuthManager, - model_family: &ModelFamily, total_usage: &TokenUsage, - context_usage: Option<&TokenUsage>, session_id: &Option, rate_limits: Option<&RateLimitSnapshotDisplay>, plan_type: Option, @@ -85,9 +74,7 @@ pub(crate) fn new_status_output( let card = StatusHistoryCell::new( config, auth_manager, - model_family, total_usage, - context_usage, session_id, rate_limits, plan_type, @@ -103,9 +90,7 @@ impl StatusHistoryCell { fn new( config: &Config, auth_manager: &AuthManager, - model_family: &ModelFamily, total_usage: &TokenUsage, - context_usage: Option<&TokenUsage>, session_id: &Option, rate_limits: Option<&RateLimitSnapshotDisplay>, plan_type: Option, @@ -134,19 +119,11 @@ impl StatusHistoryCell { let agents_summary = compose_agents_summary(config); let account = compose_account_display(auth_manager, plan_type); let session_id = session_id.as_ref().map(std::string::ToString::to_string); - let context_window = model_family.context_window.and_then(|window| { - context_usage.map(|usage| StatusContextWindowData { - percent_remaining: usage.percent_of_context_window_remaining(window), - tokens_in_context: usage.tokens_in_context_window(), - window, - }) - }); let token_usage = StatusTokenUsageData { total: total_usage.blended_total(), input: total_usage.non_cached_input(), output: total_usage.output_tokens, - context_window, }; let rate_limits = compose_rate_limit_data(rate_limits, now); @@ -182,22 +159,6 @@ impl StatusHistoryCell { ] } - fn context_window_spans(&self) -> Option>> { - let context = self.token_usage.context_window.as_ref()?; - let percent = context.percent_remaining; - let used_fmt = format_tokens_compact(context.tokens_in_context); - let window_fmt = format_tokens_compact(context.window); - - Some(vec![ - Span::from(format!("{percent}% left")), - Span::from(" (").dim(), - Span::from(used_fmt).dim(), - Span::from(" used / ").dim(), - Span::from(window_fmt).dim(), - Span::from(")").dim(), - ]) - } - fn rate_limit_lines( &self, available_inner_width: usize, @@ -345,9 +306,6 @@ impl HistoryCell for StatusHistoryCell { push_label(&mut labels, &mut seen, "Session"); } push_label(&mut labels, &mut seen, "Token usage"); - if self.token_usage.context_window.is_some() { - push_label(&mut labels, &mut seen, "Context window"); - } self.collect_rate_limit_labels(&mut seen, &mut labels); let formatter = FieldFormatter::from_labels(labels.iter().map(String::as_str)); @@ -399,10 +357,6 @@ impl HistoryCell for StatusHistoryCell { lines.push(formatter.line("Token usage", self.token_usage_spans())); } - if let Some(spans) = self.context_window_spans() { - lines.push(formatter.line("Context window", spans)); - } - lines.extend(self.rate_limit_lines(available_inner_width, &formatter)); let content_width = lines.iter().map(line_display_width).max().unwrap_or(0); diff --git a/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_cached_limits_hide_credits_without_flag.snap b/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_cached_limits_hide_credits_without_flag.snap index 5c805561461..fe5f7c67aad 100644 --- a/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_cached_limits_hide_credits_without_flag.snap +++ b/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_cached_limits_hide_credits_without_flag.snap @@ -4,21 +4,20 @@ expression: sanitized --- /status -╭─────────────────────────────────────────────────────────────────────╮ -│ >_ OpenAI Codex (v0.0.0) │ -│ │ -│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ -│ information on rate limits and credits │ -│ │ -│ Model: gpt-5.1-codex (reasoning none, summaries auto) │ -│ Directory: [[workspace]] │ -│ Approval: on-request │ -│ Sandbox: read-only │ -│ Agents.md: │ -│ │ -│ Token usage: 1.05K total (700 input + 350 output) │ -│ Context window: 100% left (1.45K used / 272K) │ -│ 5h limit: [████████░░░░░░░░░░░░] 40% left (resets 11:32) │ -│ Weekly limit: [█████████████░░░░░░░] 65% left (resets 11:52) │ -│ Warning: limits may be stale - start new turn to refresh. │ -╰─────────────────────────────────────────────────────────────────────╯ +╭───────────────────────────────────────────────────────────────────╮ +│ >_ OpenAI Codex (v0.0.0) │ +│ │ +│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ +│ information on rate limits and credits │ +│ │ +│ Model: gpt-5.1-codex (reasoning none, summaries auto) │ +│ Directory: [[workspace]] │ +│ Approval: on-request │ +│ Sandbox: read-only │ +│ Agents.md: │ +│ │ +│ Token usage: 1.05K total (700 input + 350 output) │ +│ 5h limit: [████████░░░░░░░░░░░░] 40% left (resets 11:32) │ +│ Weekly limit: [█████████████░░░░░░░] 65% left (resets 11:52) │ +│ Warning: limits may be stale - start new turn to refresh. │ +╰───────────────────────────────────────────────────────────────────╯ diff --git a/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_includes_credits_and_limits.snap b/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_includes_credits_and_limits.snap index 7a914837399..90effce5701 100644 --- a/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_includes_credits_and_limits.snap +++ b/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_includes_credits_and_limits.snap @@ -4,21 +4,20 @@ expression: sanitized --- /status -╭───────────────────────────────────────────────────────────────────╮ -│ >_ OpenAI Codex (v0.0.0) │ -│ │ -│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ -│ information on rate limits and credits │ -│ │ -│ Model: gpt-5.1-codex (reasoning none, summaries auto) │ -│ Directory: [[workspace]] │ -│ Approval: on-request │ -│ Sandbox: read-only │ -│ Agents.md: │ -│ │ -│ Token usage: 2K total (1.4K input + 600 output) │ -│ Context window: 100% left (2.2K used / 272K) │ -│ 5h limit: [███████████░░░░░░░░░] 55% left (resets 09:25) │ -│ Weekly limit: [██████████████░░░░░░] 70% left (resets 09:55) │ -│ Credits: 38 credits │ -╰───────────────────────────────────────────────────────────────────╯ +╭─────────────────────────────────────────────────────────────────╮ +│ >_ OpenAI Codex (v0.0.0) │ +│ │ +│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ +│ information on rate limits and credits │ +│ │ +│ Model: gpt-5.1-codex (reasoning none, summaries auto) │ +│ Directory: [[workspace]] │ +│ Approval: on-request │ +│ Sandbox: read-only │ +│ Agents.md: │ +│ │ +│ Token usage: 2K total (1.4K input + 600 output) │ +│ 5h limit: [███████████░░░░░░░░░] 55% left (resets 09:25) │ +│ Weekly limit: [██████████████░░░░░░] 70% left (resets 09:55) │ +│ Credits: 38 credits │ +╰─────────────────────────────────────────────────────────────────╯ diff --git a/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_includes_monthly_limit.snap b/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_includes_monthly_limit.snap index 61701111155..553d64bfb96 100644 --- a/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_includes_monthly_limit.snap +++ b/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_includes_monthly_limit.snap @@ -4,19 +4,18 @@ expression: sanitized --- /status -╭────────────────────────────────────────────────────────────────────────────╮ -│ >_ OpenAI Codex (v0.0.0) │ -│ │ -│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ -│ information on rate limits and credits │ -│ │ -│ Model: gpt-5.1-codex-max (reasoning none, summaries auto) │ -│ Directory: [[workspace]] │ -│ Approval: on-request │ -│ Sandbox: read-only │ -│ Agents.md: │ -│ │ -│ Token usage: 1.2K total (800 input + 400 output) │ -│ Context window: 100% left (1.2K used / 272K) │ -│ Monthly limit: [██████████████████░░] 88% left (resets 07:08 on 7 May) │ -╰────────────────────────────────────────────────────────────────────────────╯ +╭───────────────────────────────────────────────────────────────────────────╮ +│ >_ OpenAI Codex (v0.0.0) │ +│ │ +│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ +│ information on rate limits and credits │ +│ │ +│ Model: gpt-5.1-codex-max (reasoning none, summaries auto) │ +│ Directory: [[workspace]] │ +│ Approval: on-request │ +│ Sandbox: read-only │ +│ Agents.md: │ +│ │ +│ Token usage: 1.2K total (800 input + 400 output) │ +│ Monthly limit: [██████████████████░░] 88% left (resets 07:08 on 7 May) │ +╰───────────────────────────────────────────────────────────────────────────╯ diff --git a/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_includes_reasoning_details.snap b/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_includes_reasoning_details.snap index 1e88139cc43..8b61290748b 100644 --- a/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_includes_reasoning_details.snap +++ b/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_includes_reasoning_details.snap @@ -4,20 +4,19 @@ expression: sanitized --- /status -╭───────────────────────────────────────────────────────────────────────────╮ -│ >_ OpenAI Codex (v0.0.0) │ -│ │ -│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ -│ information on rate limits and credits │ -│ │ -│ Model: gpt-5.1-codex-max (reasoning high, summaries detailed) │ -│ Directory: [[workspace]] │ -│ Approval: on-request │ -│ Sandbox: workspace-write │ -│ Agents.md: │ -│ │ -│ Token usage: 1.9K total (1K input + 900 output) │ -│ Context window: 100% left (2.25K used / 272K) │ -│ 5h limit: [██████░░░░░░░░░░░░░░] 28% left (resets 03:14) │ -│ Weekly limit: [███████████░░░░░░░░░] 55% left (resets 03:24) │ -╰───────────────────────────────────────────────────────────────────────────╯ +╭─────────────────────────────────────────────────────────────────────────╮ +│ >_ OpenAI Codex (v0.0.0) │ +│ │ +│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ +│ information on rate limits and credits │ +│ │ +│ Model: gpt-5.1-codex-max (reasoning high, summaries detailed) │ +│ Directory: [[workspace]] │ +│ Approval: on-request │ +│ Sandbox: workspace-write │ +│ Agents.md: │ +│ │ +│ Token usage: 1.9K total (1K input + 900 output) │ +│ 5h limit: [██████░░░░░░░░░░░░░░] 28% left (resets 03:14) │ +│ Weekly limit: [███████████░░░░░░░░░] 55% left (resets 03:24) │ +╰─────────────────────────────────────────────────────────────────────────╯ diff --git a/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_shows_empty_limits_message.snap b/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_shows_empty_limits_message.snap index ac824827e3a..bd636c96b89 100644 --- a/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_shows_empty_limits_message.snap +++ b/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_shows_empty_limits_message.snap @@ -4,19 +4,18 @@ expression: sanitized --- /status -╭───────────────────────────────────────────────────────────────────────╮ -│ >_ OpenAI Codex (v0.0.0) │ -│ │ -│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ -│ information on rate limits and credits │ -│ │ -│ Model: gpt-5.1-codex-max (reasoning none, summaries auto) │ -│ Directory: [[workspace]] │ -│ Approval: on-request │ -│ Sandbox: read-only │ -│ Agents.md: │ -│ │ -│ Token usage: 750 total (500 input + 250 output) │ -│ Context window: 100% left (750 used / 272K) │ -│ Limits: data not available yet │ -╰───────────────────────────────────────────────────────────────────────╯ +╭────────────────────────────────────────────────────────────────────╮ +│ >_ OpenAI Codex (v0.0.0) │ +│ │ +│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ +│ information on rate limits and credits │ +│ │ +│ Model: gpt-5.1-codex-max (reasoning none, summaries auto) │ +│ Directory: [[workspace]] │ +│ Approval: on-request │ +│ Sandbox: read-only │ +│ Agents.md: │ +│ │ +│ Token usage: 750 total (500 input + 250 output) │ +│ Limits: data not available yet │ +╰────────────────────────────────────────────────────────────────────╯ diff --git a/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_shows_missing_limits_message.snap b/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_shows_missing_limits_message.snap index ac824827e3a..bd636c96b89 100644 --- a/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_shows_missing_limits_message.snap +++ b/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_shows_missing_limits_message.snap @@ -4,19 +4,18 @@ expression: sanitized --- /status -╭───────────────────────────────────────────────────────────────────────╮ -│ >_ OpenAI Codex (v0.0.0) │ -│ │ -│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ -│ information on rate limits and credits │ -│ │ -│ Model: gpt-5.1-codex-max (reasoning none, summaries auto) │ -│ Directory: [[workspace]] │ -│ Approval: on-request │ -│ Sandbox: read-only │ -│ Agents.md: │ -│ │ -│ Token usage: 750 total (500 input + 250 output) │ -│ Context window: 100% left (750 used / 272K) │ -│ Limits: data not available yet │ -╰───────────────────────────────────────────────────────────────────────╯ +╭────────────────────────────────────────────────────────────────────╮ +│ >_ OpenAI Codex (v0.0.0) │ +│ │ +│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ +│ information on rate limits and credits │ +│ │ +│ Model: gpt-5.1-codex-max (reasoning none, summaries auto) │ +│ Directory: [[workspace]] │ +│ Approval: on-request │ +│ Sandbox: read-only │ +│ Agents.md: │ +│ │ +│ Token usage: 750 total (500 input + 250 output) │ +│ Limits: data not available yet │ +╰────────────────────────────────────────────────────────────────────╯ diff --git a/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_shows_stale_limits_message.snap b/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_shows_stale_limits_message.snap index ffdb825bac6..63a43914b1a 100644 --- a/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_shows_stale_limits_message.snap +++ b/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_shows_stale_limits_message.snap @@ -4,21 +4,20 @@ expression: sanitized --- /status -╭───────────────────────────────────────────────────────────────────────╮ -│ >_ OpenAI Codex (v0.0.0) │ -│ │ -│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ -│ information on rate limits and credits │ -│ │ -│ Model: gpt-5.1-codex-max (reasoning none, summaries auto) │ -│ Directory: [[workspace]] │ -│ Approval: on-request │ -│ Sandbox: read-only │ -│ Agents.md: │ -│ │ -│ Token usage: 1.9K total (1K input + 900 output) │ -│ Context window: 100% left (2.25K used / 272K) │ -│ 5h limit: [██████░░░░░░░░░░░░░░] 28% left (resets 03:14) │ -│ Weekly limit: [████████████░░░░░░░░] 60% left (resets 03:34) │ -│ Warning: limits may be stale - start new turn to refresh. │ -╰───────────────────────────────────────────────────────────────────────╯ +╭─────────────────────────────────────────────────────────────────────╮ +│ >_ OpenAI Codex (v0.0.0) │ +│ │ +│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ +│ information on rate limits and credits │ +│ │ +│ Model: gpt-5.1-codex-max (reasoning none, summaries auto) │ +│ Directory: [[workspace]] │ +│ Approval: on-request │ +│ Sandbox: read-only │ +│ Agents.md: │ +│ │ +│ Token usage: 1.9K total (1K input + 900 output) │ +│ 5h limit: [██████░░░░░░░░░░░░░░] 28% left (resets 03:14) │ +│ Weekly limit: [████████████░░░░░░░░] 60% left (resets 03:34) │ +│ Warning: limits may be stale - start new turn to refresh. │ +╰─────────────────────────────────────────────────────────────────────╯ diff --git a/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_truncates_in_narrow_terminal.snap b/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_truncates_in_narrow_terminal.snap index 1762b1b715f..ef2303b5f1c 100644 --- a/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_truncates_in_narrow_terminal.snap +++ b/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_truncates_in_narrow_terminal.snap @@ -10,13 +10,12 @@ expression: sanitized │ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ │ information on rate limits and credits │ │ │ -│ Model: gpt-5.1-codex-max (reasoning high, summaries de │ +│ Model: gpt-5.1-codex-max (reasoning high, summaries detai │ │ Directory: [[workspace]] │ -│ Approval: on-request │ -│ Sandbox: read-only │ -│ Agents.md: │ +│ Approval: on-request │ +│ Sandbox: read-only │ +│ Agents.md: │ │ │ -│ Token usage: 1.9K total (1K input + 900 output) │ -│ Context window: 100% left (2.25K used / 272K) │ -│ 5h limit: [██████░░░░░░░░░░░░░░] 28% left (resets 03:14) │ +│ Token usage: 1.9K total (1K input + 900 output) │ +│ 5h limit: [██████░░░░░░░░░░░░░░] 28% left (resets 03:14) │ ╰────────────────────────────────────────────────────────────────────╯ diff --git a/codex-rs/tui2/src/status/tests.rs b/codex-rs/tui2/src/status/tests.rs index 317a3d32703..2a9d5bc77ae 100644 --- a/codex-rs/tui2/src/status/tests.rs +++ b/codex-rs/tui2/src/status/tests.rs @@ -8,7 +8,6 @@ use codex_core::AuthManager; use codex_core::config::Config; use codex_core::config::ConfigBuilder; use codex_core::models_manager::manager::ModelsManager; -use codex_core::models_manager::model_family::ModelFamily; use codex_core::protocol::CreditsSnapshot; use codex_core::protocol::RateLimitSnapshot; use codex_core::protocol::RateLimitWindow; @@ -37,10 +36,6 @@ fn test_auth_manager(config: &Config) -> AuthManager { ) } -fn test_model_family(model_slug: &str, config: &Config) -> ModelFamily { - ModelsManager::construct_model_family_offline(model_slug, config) -} - fn render_lines(lines: &[Line<'static>]) -> Vec { lines .iter() @@ -132,14 +127,11 @@ async fn status_snapshot_includes_reasoning_details() { let rate_display = rate_limit_snapshot_display(&snapshot, captured_at); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); - let model_family = test_model_family(&model_slug, &config); let composite = new_status_output( &config, &auth_manager, - &model_family, &usage, - Some(&usage), &None, Some(&rate_display), None, @@ -155,7 +147,6 @@ async fn status_snapshot_includes_reasoning_details() { let sanitized = sanitize_directory(rendered_lines).join("\n"); assert_snapshot!(sanitized); } - #[tokio::test] async fn status_snapshot_includes_monthly_limit() { let temp_home = TempDir::new().expect("temp home"); @@ -190,13 +181,10 @@ async fn status_snapshot_includes_monthly_limit() { let rate_display = rate_limit_snapshot_display(&snapshot, captured_at); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); - let model_family = test_model_family(&model_slug, &config); let composite = new_status_output( &config, &auth_manager, - &model_family, &usage, - Some(&usage), &None, Some(&rate_display), None, @@ -212,7 +200,6 @@ async fn status_snapshot_includes_monthly_limit() { let sanitized = sanitize_directory(rendered_lines).join("\n"); assert_snapshot!(sanitized); } - #[tokio::test] async fn status_snapshot_shows_unlimited_credits() { let temp_home = TempDir::new().expect("temp home"); @@ -235,13 +222,10 @@ async fn status_snapshot_shows_unlimited_credits() { }; let rate_display = rate_limit_snapshot_display(&snapshot, captured_at); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); - let model_family = test_model_family(&model_slug, &config); let composite = new_status_output( &config, &auth_manager, - &model_family, &usage, - Some(&usage), &None, Some(&rate_display), None, @@ -279,13 +263,10 @@ async fn status_snapshot_shows_positive_credits() { }; let rate_display = rate_limit_snapshot_display(&snapshot, captured_at); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); - let model_family = test_model_family(&model_slug, &config); let composite = new_status_output( &config, &auth_manager, - &model_family, &usage, - Some(&usage), &None, Some(&rate_display), None, @@ -323,13 +304,10 @@ async fn status_snapshot_hides_zero_credits() { }; let rate_display = rate_limit_snapshot_display(&snapshot, captured_at); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); - let model_family = test_model_family(&model_slug, &config); let composite = new_status_output( &config, &auth_manager, - &model_family, &usage, - Some(&usage), &None, Some(&rate_display), None, @@ -365,13 +343,10 @@ async fn status_snapshot_hides_when_has_no_credits_flag() { }; let rate_display = rate_limit_snapshot_display(&snapshot, captured_at); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); - let model_family = test_model_family(&model_slug, &config); let composite = new_status_output( &config, &auth_manager, - &model_family, &usage, - Some(&usage), &None, Some(&rate_display), None, @@ -407,13 +382,10 @@ async fn status_card_token_usage_excludes_cached_tokens() { .expect("timestamp"); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); - let model_family = test_model_family(&model_slug, &config); let composite = new_status_output( &config, &auth_manager, - &model_family, &usage, - Some(&usage), &None, None, None, @@ -464,13 +436,10 @@ async fn status_snapshot_truncates_in_narrow_terminal() { let rate_display = rate_limit_snapshot_display(&snapshot, captured_at); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); - let model_family = test_model_family(&model_slug, &config); let composite = new_status_output( &config, &auth_manager, - &model_family, &usage, - Some(&usage), &None, Some(&rate_display), None, @@ -510,13 +479,10 @@ async fn status_snapshot_shows_missing_limits_message() { .expect("timestamp"); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); - let model_family = test_model_family(&model_slug, &config); let composite = new_status_output( &config, &auth_manager, - &model_family, &usage, - Some(&usage), &None, None, None, @@ -532,7 +498,6 @@ async fn status_snapshot_shows_missing_limits_message() { let sanitized = sanitize_directory(rendered_lines).join("\n"); assert_snapshot!(sanitized); } - #[tokio::test] async fn status_snapshot_includes_credits_and_limits() { let temp_home = TempDir::new().expect("temp home"); @@ -574,13 +539,10 @@ async fn status_snapshot_includes_credits_and_limits() { let rate_display = rate_limit_snapshot_display(&snapshot, captured_at); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); - let model_family = test_model_family(&model_slug, &config); let composite = new_status_output( &config, &auth_manager, - &model_family, &usage, - Some(&usage), &None, Some(&rate_display), None, @@ -626,13 +588,10 @@ async fn status_snapshot_shows_empty_limits_message() { let rate_display = rate_limit_snapshot_display(&snapshot, captured_at); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); - let model_family = test_model_family(&model_slug, &config); let composite = new_status_output( &config, &auth_manager, - &model_family, &usage, - Some(&usage), &None, Some(&rate_display), None, @@ -687,13 +646,10 @@ async fn status_snapshot_shows_stale_limits_message() { let now = captured_at + ChronoDuration::minutes(20); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); - let model_family = test_model_family(&model_slug, &config); let composite = new_status_output( &config, &auth_manager, - &model_family, &usage, - Some(&usage), &None, Some(&rate_display), None, @@ -752,13 +708,10 @@ async fn status_snapshot_cached_limits_hide_credits_without_flag() { let now = captured_at + ChronoDuration::minutes(20); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); - let model_family = test_model_family(&model_slug, &config); let composite = new_status_output( &config, &auth_manager, - &model_family, &usage, - Some(&usage), &None, Some(&rate_display), None, @@ -774,60 +727,3 @@ async fn status_snapshot_cached_limits_hide_credits_without_flag() { let sanitized = sanitize_directory(rendered_lines).join("\n"); assert_snapshot!(sanitized); } - -#[tokio::test] -async fn status_context_window_uses_last_usage() { - let temp_home = TempDir::new().expect("temp home"); - let mut config = test_config(&temp_home).await; - config.model_context_window = Some(272_000); - - let auth_manager = test_auth_manager(&config); - let total_usage = TokenUsage { - input_tokens: 12_800, - cached_input_tokens: 0, - output_tokens: 879, - reasoning_output_tokens: 0, - total_tokens: 102_000, - }; - let last_usage = TokenUsage { - input_tokens: 12_800, - cached_input_tokens: 0, - output_tokens: 879, - reasoning_output_tokens: 0, - total_tokens: 13_679, - }; - - let now = chrono::Local - .with_ymd_and_hms(2024, 6, 1, 12, 0, 0) - .single() - .expect("timestamp"); - - let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); - let model_family = test_model_family(&model_slug, &config); - let composite = new_status_output( - &config, - &auth_manager, - &model_family, - &total_usage, - Some(&last_usage), - &None, - None, - None, - now, - &model_slug, - ); - let rendered_lines = render_lines(&composite.display_lines(80)); - let context_line = rendered_lines - .into_iter() - .find(|line| line.contains("Context window")) - .expect("context line"); - - assert!( - context_line.contains("13.7K used / 272K"), - "expected context line to reflect last usage tokens, got: {context_line}" - ); - assert!( - !context_line.contains("102K"), - "context line should not use total aggregated tokens, got: {context_line}" - ); -} From 8a0a8ac87427f44ee76e1d9b7ae2ab2d5a0b6c9a Mon Sep 17 00:00:00 2001 From: Ahmed Ibrahim Date: Wed, 31 Dec 2025 23:11:49 -0800 Subject: [PATCH 6/8] context window in status --- codex-rs/tui2/src/chatwidget.rs | 10 +- codex-rs/tui2/src/status/card.rs | 46 +++++++++ ...ched_limits_hide_credits_without_flag.snap | 35 +++---- ..._snapshot_includes_credits_and_limits.snap | 35 +++---- ...tatus_snapshot_includes_monthly_limit.snap | 31 +++--- ...s_snapshot_includes_reasoning_details.snap | 33 ++++--- ...s_snapshot_shows_empty_limits_message.snap | 31 +++--- ...snapshot_shows_missing_limits_message.snap | 31 +++--- ...s_snapshot_shows_stale_limits_message.snap | 35 +++---- ...snapshot_truncates_in_narrow_terminal.snap | 13 +-- codex-rs/tui2/src/status/tests.rs | 98 +++++++++++++++++++ 11 files changed, 275 insertions(+), 123 deletions(-) diff --git a/codex-rs/tui2/src/chatwidget.rs b/codex-rs/tui2/src/chatwidget.rs index 7a1e601c718..799e7f99342 100644 --- a/codex-rs/tui2/src/chatwidget.rs +++ b/codex-rs/tui2/src/chatwidget.rs @@ -2042,14 +2042,14 @@ impl ChatWidget { pub(crate) fn add_status_output(&mut self) { let default_usage = TokenUsage::default(); - let total_usage = if let Some(ti) = &self.token_info { - &ti.total_token_usage - } else { - &default_usage - }; + let token_info = self.token_info.as_ref(); + let total_usage = token_info + .map(|ti| &ti.total_token_usage) + .unwrap_or(&default_usage); self.add_to_history(crate::status::new_status_output( &self.config, self.auth_manager.as_ref(), + token_info, total_usage, &self.conversation_id, self.rate_limit_snapshot.as_ref(), diff --git a/codex-rs/tui2/src/status/card.rs b/codex-rs/tui2/src/status/card.rs index e795647cf5f..3e7a626e407 100644 --- a/codex-rs/tui2/src/status/card.rs +++ b/codex-rs/tui2/src/status/card.rs @@ -10,6 +10,7 @@ use codex_core::config::Config; use codex_core::protocol::NetworkAccess; use codex_core::protocol::SandboxPolicy; use codex_core::protocol::TokenUsage; +use codex_core::protocol::TokenUsageInfo; use codex_protocol::ConversationId; use codex_protocol::account::PlanType; use ratatui::prelude::*; @@ -38,11 +39,19 @@ use crate::wrapping::RtOptions; use crate::wrapping::word_wrap_lines; use codex_core::AuthManager; +#[derive(Debug, Clone)] +struct StatusContextWindowData { + percent_remaining: i64, + tokens_in_context: i64, + window: i64, +} + #[derive(Debug, Clone)] pub(crate) struct StatusTokenUsageData { total: i64, input: i64, output: i64, + context_window: Option, } #[derive(Debug)] @@ -63,6 +72,7 @@ struct StatusHistoryCell { pub(crate) fn new_status_output( config: &Config, auth_manager: &AuthManager, + token_info: Option<&TokenUsageInfo>, total_usage: &TokenUsage, session_id: &Option, rate_limits: Option<&RateLimitSnapshotDisplay>, @@ -74,6 +84,7 @@ pub(crate) fn new_status_output( let card = StatusHistoryCell::new( config, auth_manager, + token_info, total_usage, session_id, rate_limits, @@ -90,6 +101,7 @@ impl StatusHistoryCell { fn new( config: &Config, auth_manager: &AuthManager, + token_info: Option<&TokenUsageInfo>, total_usage: &TokenUsage, session_id: &Option, rate_limits: Option<&RateLimitSnapshotDisplay>, @@ -119,11 +131,22 @@ impl StatusHistoryCell { let agents_summary = compose_agents_summary(config); let account = compose_account_display(auth_manager, plan_type); let session_id = session_id.as_ref().map(std::string::ToString::to_string); + let default_usage = TokenUsage::default(); + let (context_usage, context_window) = match token_info { + Some(info) => (&info.last_token_usage, info.model_context_window), + None => (&default_usage, config.model_context_window), + }; + let context_window = context_window.map(|window| StatusContextWindowData { + percent_remaining: context_usage.percent_of_context_window_remaining(window), + tokens_in_context: context_usage.tokens_in_context_window(), + window, + }); let token_usage = StatusTokenUsageData { total: total_usage.blended_total(), input: total_usage.non_cached_input(), output: total_usage.output_tokens, + context_window, }; let rate_limits = compose_rate_limit_data(rate_limits, now); @@ -159,6 +182,22 @@ impl StatusHistoryCell { ] } + fn context_window_spans(&self) -> Option>> { + let context = self.token_usage.context_window.as_ref()?; + let percent = context.percent_remaining; + let used_fmt = format_tokens_compact(context.tokens_in_context); + let window_fmt = format_tokens_compact(context.window); + + Some(vec![ + Span::from(format!("{percent}% left")), + Span::from(" (").dim(), + Span::from(used_fmt).dim(), + Span::from(" used / ").dim(), + Span::from(window_fmt).dim(), + Span::from(")").dim(), + ]) + } + fn rate_limit_lines( &self, available_inner_width: usize, @@ -306,6 +345,9 @@ impl HistoryCell for StatusHistoryCell { push_label(&mut labels, &mut seen, "Session"); } push_label(&mut labels, &mut seen, "Token usage"); + if self.token_usage.context_window.is_some() { + push_label(&mut labels, &mut seen, "Context window"); + } self.collect_rate_limit_labels(&mut seen, &mut labels); let formatter = FieldFormatter::from_labels(labels.iter().map(String::as_str)); @@ -357,6 +399,10 @@ impl HistoryCell for StatusHistoryCell { lines.push(formatter.line("Token usage", self.token_usage_spans())); } + if let Some(spans) = self.context_window_spans() { + lines.push(formatter.line("Context window", spans)); + } + lines.extend(self.rate_limit_lines(available_inner_width, &formatter)); let content_width = lines.iter().map(line_display_width).max().unwrap_or(0); diff --git a/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_cached_limits_hide_credits_without_flag.snap b/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_cached_limits_hide_credits_without_flag.snap index fe5f7c67aad..5c805561461 100644 --- a/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_cached_limits_hide_credits_without_flag.snap +++ b/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_cached_limits_hide_credits_without_flag.snap @@ -4,20 +4,21 @@ expression: sanitized --- /status -╭───────────────────────────────────────────────────────────────────╮ -│ >_ OpenAI Codex (v0.0.0) │ -│ │ -│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ -│ information on rate limits and credits │ -│ │ -│ Model: gpt-5.1-codex (reasoning none, summaries auto) │ -│ Directory: [[workspace]] │ -│ Approval: on-request │ -│ Sandbox: read-only │ -│ Agents.md: │ -│ │ -│ Token usage: 1.05K total (700 input + 350 output) │ -│ 5h limit: [████████░░░░░░░░░░░░] 40% left (resets 11:32) │ -│ Weekly limit: [█████████████░░░░░░░] 65% left (resets 11:52) │ -│ Warning: limits may be stale - start new turn to refresh. │ -╰───────────────────────────────────────────────────────────────────╯ +╭─────────────────────────────────────────────────────────────────────╮ +│ >_ OpenAI Codex (v0.0.0) │ +│ │ +│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ +│ information on rate limits and credits │ +│ │ +│ Model: gpt-5.1-codex (reasoning none, summaries auto) │ +│ Directory: [[workspace]] │ +│ Approval: on-request │ +│ Sandbox: read-only │ +│ Agents.md: │ +│ │ +│ Token usage: 1.05K total (700 input + 350 output) │ +│ Context window: 100% left (1.45K used / 272K) │ +│ 5h limit: [████████░░░░░░░░░░░░] 40% left (resets 11:32) │ +│ Weekly limit: [█████████████░░░░░░░] 65% left (resets 11:52) │ +│ Warning: limits may be stale - start new turn to refresh. │ +╰─────────────────────────────────────────────────────────────────────╯ diff --git a/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_includes_credits_and_limits.snap b/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_includes_credits_and_limits.snap index 90effce5701..7a914837399 100644 --- a/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_includes_credits_and_limits.snap +++ b/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_includes_credits_and_limits.snap @@ -4,20 +4,21 @@ expression: sanitized --- /status -╭─────────────────────────────────────────────────────────────────╮ -│ >_ OpenAI Codex (v0.0.0) │ -│ │ -│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ -│ information on rate limits and credits │ -│ │ -│ Model: gpt-5.1-codex (reasoning none, summaries auto) │ -│ Directory: [[workspace]] │ -│ Approval: on-request │ -│ Sandbox: read-only │ -│ Agents.md: │ -│ │ -│ Token usage: 2K total (1.4K input + 600 output) │ -│ 5h limit: [███████████░░░░░░░░░] 55% left (resets 09:25) │ -│ Weekly limit: [██████████████░░░░░░] 70% left (resets 09:55) │ -│ Credits: 38 credits │ -╰─────────────────────────────────────────────────────────────────╯ +╭───────────────────────────────────────────────────────────────────╮ +│ >_ OpenAI Codex (v0.0.0) │ +│ │ +│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ +│ information on rate limits and credits │ +│ │ +│ Model: gpt-5.1-codex (reasoning none, summaries auto) │ +│ Directory: [[workspace]] │ +│ Approval: on-request │ +│ Sandbox: read-only │ +│ Agents.md: │ +│ │ +│ Token usage: 2K total (1.4K input + 600 output) │ +│ Context window: 100% left (2.2K used / 272K) │ +│ 5h limit: [███████████░░░░░░░░░] 55% left (resets 09:25) │ +│ Weekly limit: [██████████████░░░░░░] 70% left (resets 09:55) │ +│ Credits: 38 credits │ +╰───────────────────────────────────────────────────────────────────╯ diff --git a/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_includes_monthly_limit.snap b/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_includes_monthly_limit.snap index 553d64bfb96..61701111155 100644 --- a/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_includes_monthly_limit.snap +++ b/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_includes_monthly_limit.snap @@ -4,18 +4,19 @@ expression: sanitized --- /status -╭───────────────────────────────────────────────────────────────────────────╮ -│ >_ OpenAI Codex (v0.0.0) │ -│ │ -│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ -│ information on rate limits and credits │ -│ │ -│ Model: gpt-5.1-codex-max (reasoning none, summaries auto) │ -│ Directory: [[workspace]] │ -│ Approval: on-request │ -│ Sandbox: read-only │ -│ Agents.md: │ -│ │ -│ Token usage: 1.2K total (800 input + 400 output) │ -│ Monthly limit: [██████████████████░░] 88% left (resets 07:08 on 7 May) │ -╰───────────────────────────────────────────────────────────────────────────╯ +╭────────────────────────────────────────────────────────────────────────────╮ +│ >_ OpenAI Codex (v0.0.0) │ +│ │ +│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ +│ information on rate limits and credits │ +│ │ +│ Model: gpt-5.1-codex-max (reasoning none, summaries auto) │ +│ Directory: [[workspace]] │ +│ Approval: on-request │ +│ Sandbox: read-only │ +│ Agents.md: │ +│ │ +│ Token usage: 1.2K total (800 input + 400 output) │ +│ Context window: 100% left (1.2K used / 272K) │ +│ Monthly limit: [██████████████████░░] 88% left (resets 07:08 on 7 May) │ +╰────────────────────────────────────────────────────────────────────────────╯ diff --git a/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_includes_reasoning_details.snap b/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_includes_reasoning_details.snap index 8b61290748b..1e88139cc43 100644 --- a/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_includes_reasoning_details.snap +++ b/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_includes_reasoning_details.snap @@ -4,19 +4,20 @@ expression: sanitized --- /status -╭─────────────────────────────────────────────────────────────────────────╮ -│ >_ OpenAI Codex (v0.0.0) │ -│ │ -│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ -│ information on rate limits and credits │ -│ │ -│ Model: gpt-5.1-codex-max (reasoning high, summaries detailed) │ -│ Directory: [[workspace]] │ -│ Approval: on-request │ -│ Sandbox: workspace-write │ -│ Agents.md: │ -│ │ -│ Token usage: 1.9K total (1K input + 900 output) │ -│ 5h limit: [██████░░░░░░░░░░░░░░] 28% left (resets 03:14) │ -│ Weekly limit: [███████████░░░░░░░░░] 55% left (resets 03:24) │ -╰─────────────────────────────────────────────────────────────────────────╯ +╭───────────────────────────────────────────────────────────────────────────╮ +│ >_ OpenAI Codex (v0.0.0) │ +│ │ +│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ +│ information on rate limits and credits │ +│ │ +│ Model: gpt-5.1-codex-max (reasoning high, summaries detailed) │ +│ Directory: [[workspace]] │ +│ Approval: on-request │ +│ Sandbox: workspace-write │ +│ Agents.md: │ +│ │ +│ Token usage: 1.9K total (1K input + 900 output) │ +│ Context window: 100% left (2.25K used / 272K) │ +│ 5h limit: [██████░░░░░░░░░░░░░░] 28% left (resets 03:14) │ +│ Weekly limit: [███████████░░░░░░░░░] 55% left (resets 03:24) │ +╰───────────────────────────────────────────────────────────────────────────╯ diff --git a/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_shows_empty_limits_message.snap b/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_shows_empty_limits_message.snap index bd636c96b89..ac824827e3a 100644 --- a/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_shows_empty_limits_message.snap +++ b/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_shows_empty_limits_message.snap @@ -4,18 +4,19 @@ expression: sanitized --- /status -╭────────────────────────────────────────────────────────────────────╮ -│ >_ OpenAI Codex (v0.0.0) │ -│ │ -│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ -│ information on rate limits and credits │ -│ │ -│ Model: gpt-5.1-codex-max (reasoning none, summaries auto) │ -│ Directory: [[workspace]] │ -│ Approval: on-request │ -│ Sandbox: read-only │ -│ Agents.md: │ -│ │ -│ Token usage: 750 total (500 input + 250 output) │ -│ Limits: data not available yet │ -╰────────────────────────────────────────────────────────────────────╯ +╭───────────────────────────────────────────────────────────────────────╮ +│ >_ OpenAI Codex (v0.0.0) │ +│ │ +│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ +│ information on rate limits and credits │ +│ │ +│ Model: gpt-5.1-codex-max (reasoning none, summaries auto) │ +│ Directory: [[workspace]] │ +│ Approval: on-request │ +│ Sandbox: read-only │ +│ Agents.md: │ +│ │ +│ Token usage: 750 total (500 input + 250 output) │ +│ Context window: 100% left (750 used / 272K) │ +│ Limits: data not available yet │ +╰───────────────────────────────────────────────────────────────────────╯ diff --git a/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_shows_missing_limits_message.snap b/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_shows_missing_limits_message.snap index bd636c96b89..ac824827e3a 100644 --- a/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_shows_missing_limits_message.snap +++ b/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_shows_missing_limits_message.snap @@ -4,18 +4,19 @@ expression: sanitized --- /status -╭────────────────────────────────────────────────────────────────────╮ -│ >_ OpenAI Codex (v0.0.0) │ -│ │ -│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ -│ information on rate limits and credits │ -│ │ -│ Model: gpt-5.1-codex-max (reasoning none, summaries auto) │ -│ Directory: [[workspace]] │ -│ Approval: on-request │ -│ Sandbox: read-only │ -│ Agents.md: │ -│ │ -│ Token usage: 750 total (500 input + 250 output) │ -│ Limits: data not available yet │ -╰────────────────────────────────────────────────────────────────────╯ +╭───────────────────────────────────────────────────────────────────────╮ +│ >_ OpenAI Codex (v0.0.0) │ +│ │ +│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ +│ information on rate limits and credits │ +│ │ +│ Model: gpt-5.1-codex-max (reasoning none, summaries auto) │ +│ Directory: [[workspace]] │ +│ Approval: on-request │ +│ Sandbox: read-only │ +│ Agents.md: │ +│ │ +│ Token usage: 750 total (500 input + 250 output) │ +│ Context window: 100% left (750 used / 272K) │ +│ Limits: data not available yet │ +╰───────────────────────────────────────────────────────────────────────╯ diff --git a/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_shows_stale_limits_message.snap b/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_shows_stale_limits_message.snap index 63a43914b1a..ffdb825bac6 100644 --- a/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_shows_stale_limits_message.snap +++ b/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_shows_stale_limits_message.snap @@ -4,20 +4,21 @@ expression: sanitized --- /status -╭─────────────────────────────────────────────────────────────────────╮ -│ >_ OpenAI Codex (v0.0.0) │ -│ │ -│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ -│ information on rate limits and credits │ -│ │ -│ Model: gpt-5.1-codex-max (reasoning none, summaries auto) │ -│ Directory: [[workspace]] │ -│ Approval: on-request │ -│ Sandbox: read-only │ -│ Agents.md: │ -│ │ -│ Token usage: 1.9K total (1K input + 900 output) │ -│ 5h limit: [██████░░░░░░░░░░░░░░] 28% left (resets 03:14) │ -│ Weekly limit: [████████████░░░░░░░░] 60% left (resets 03:34) │ -│ Warning: limits may be stale - start new turn to refresh. │ -╰─────────────────────────────────────────────────────────────────────╯ +╭───────────────────────────────────────────────────────────────────────╮ +│ >_ OpenAI Codex (v0.0.0) │ +│ │ +│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ +│ information on rate limits and credits │ +│ │ +│ Model: gpt-5.1-codex-max (reasoning none, summaries auto) │ +│ Directory: [[workspace]] │ +│ Approval: on-request │ +│ Sandbox: read-only │ +│ Agents.md: │ +│ │ +│ Token usage: 1.9K total (1K input + 900 output) │ +│ Context window: 100% left (2.25K used / 272K) │ +│ 5h limit: [██████░░░░░░░░░░░░░░] 28% left (resets 03:14) │ +│ Weekly limit: [████████████░░░░░░░░] 60% left (resets 03:34) │ +│ Warning: limits may be stale - start new turn to refresh. │ +╰───────────────────────────────────────────────────────────────────────╯ diff --git a/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_truncates_in_narrow_terminal.snap b/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_truncates_in_narrow_terminal.snap index ef2303b5f1c..1762b1b715f 100644 --- a/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_truncates_in_narrow_terminal.snap +++ b/codex-rs/tui2/src/status/snapshots/codex_tui2__status__tests__status_snapshot_truncates_in_narrow_terminal.snap @@ -10,12 +10,13 @@ expression: sanitized │ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ │ information on rate limits and credits │ │ │ -│ Model: gpt-5.1-codex-max (reasoning high, summaries detai │ +│ Model: gpt-5.1-codex-max (reasoning high, summaries de │ │ Directory: [[workspace]] │ -│ Approval: on-request │ -│ Sandbox: read-only │ -│ Agents.md: │ +│ Approval: on-request │ +│ Sandbox: read-only │ +│ Agents.md: │ │ │ -│ Token usage: 1.9K total (1K input + 900 output) │ -│ 5h limit: [██████░░░░░░░░░░░░░░] 28% left (resets 03:14) │ +│ Token usage: 1.9K total (1K input + 900 output) │ +│ Context window: 100% left (2.25K used / 272K) │ +│ 5h limit: [██████░░░░░░░░░░░░░░] 28% left (resets 03:14) │ ╰────────────────────────────────────────────────────────────────────╯ diff --git a/codex-rs/tui2/src/status/tests.rs b/codex-rs/tui2/src/status/tests.rs index 2a9d5bc77ae..bae9dfdbd15 100644 --- a/codex-rs/tui2/src/status/tests.rs +++ b/codex-rs/tui2/src/status/tests.rs @@ -13,6 +13,7 @@ use codex_core::protocol::RateLimitSnapshot; use codex_core::protocol::RateLimitWindow; use codex_core::protocol::SandboxPolicy; use codex_core::protocol::TokenUsage; +use codex_core::protocol::TokenUsageInfo; use codex_protocol::config_types::ReasoningSummary; use codex_protocol::openai_models::ReasoningEffort; use insta::assert_snapshot; @@ -36,6 +37,17 @@ fn test_auth_manager(config: &Config) -> AuthManager { ) } +fn token_info_for(model_slug: &str, config: &Config, usage: &TokenUsage) -> TokenUsageInfo { + let context_window = ModelsManager::construct_model_family_offline(model_slug, config) + .context_window + .or(config.model_context_window); + TokenUsageInfo { + total_token_usage: usage.clone(), + last_token_usage: usage.clone(), + model_context_window: context_window, + } +} + fn render_lines(lines: &[Line<'static>]) -> Vec { lines .iter() @@ -127,10 +139,12 @@ async fn status_snapshot_includes_reasoning_details() { let rate_display = rate_limit_snapshot_display(&snapshot, captured_at); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); + let token_info = token_info_for(&model_slug, &config, &usage); let composite = new_status_output( &config, &auth_manager, + Some(&token_info), &usage, &None, Some(&rate_display), @@ -181,9 +195,11 @@ async fn status_snapshot_includes_monthly_limit() { let rate_display = rate_limit_snapshot_display(&snapshot, captured_at); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); + let token_info = token_info_for(&model_slug, &config, &usage); let composite = new_status_output( &config, &auth_manager, + Some(&token_info), &usage, &None, Some(&rate_display), @@ -222,9 +238,11 @@ async fn status_snapshot_shows_unlimited_credits() { }; let rate_display = rate_limit_snapshot_display(&snapshot, captured_at); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); + let token_info = token_info_for(&model_slug, &config, &usage); let composite = new_status_output( &config, &auth_manager, + Some(&token_info), &usage, &None, Some(&rate_display), @@ -263,9 +281,11 @@ async fn status_snapshot_shows_positive_credits() { }; let rate_display = rate_limit_snapshot_display(&snapshot, captured_at); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); + let token_info = token_info_for(&model_slug, &config, &usage); let composite = new_status_output( &config, &auth_manager, + Some(&token_info), &usage, &None, Some(&rate_display), @@ -304,9 +324,11 @@ async fn status_snapshot_hides_zero_credits() { }; let rate_display = rate_limit_snapshot_display(&snapshot, captured_at); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); + let token_info = token_info_for(&model_slug, &config, &usage); let composite = new_status_output( &config, &auth_manager, + Some(&token_info), &usage, &None, Some(&rate_display), @@ -343,9 +365,11 @@ async fn status_snapshot_hides_when_has_no_credits_flag() { }; let rate_display = rate_limit_snapshot_display(&snapshot, captured_at); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); + let token_info = token_info_for(&model_slug, &config, &usage); let composite = new_status_output( &config, &auth_manager, + Some(&token_info), &usage, &None, Some(&rate_display), @@ -382,9 +406,11 @@ async fn status_card_token_usage_excludes_cached_tokens() { .expect("timestamp"); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); + let token_info = token_info_for(&model_slug, &config, &usage); let composite = new_status_output( &config, &auth_manager, + Some(&token_info), &usage, &None, None, @@ -436,9 +462,11 @@ async fn status_snapshot_truncates_in_narrow_terminal() { let rate_display = rate_limit_snapshot_display(&snapshot, captured_at); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); + let token_info = token_info_for(&model_slug, &config, &usage); let composite = new_status_output( &config, &auth_manager, + Some(&token_info), &usage, &None, Some(&rate_display), @@ -479,9 +507,11 @@ async fn status_snapshot_shows_missing_limits_message() { .expect("timestamp"); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); + let token_info = token_info_for(&model_slug, &config, &usage); let composite = new_status_output( &config, &auth_manager, + Some(&token_info), &usage, &None, None, @@ -539,9 +569,11 @@ async fn status_snapshot_includes_credits_and_limits() { let rate_display = rate_limit_snapshot_display(&snapshot, captured_at); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); + let token_info = token_info_for(&model_slug, &config, &usage); let composite = new_status_output( &config, &auth_manager, + Some(&token_info), &usage, &None, Some(&rate_display), @@ -588,9 +620,11 @@ async fn status_snapshot_shows_empty_limits_message() { let rate_display = rate_limit_snapshot_display(&snapshot, captured_at); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); + let token_info = token_info_for(&model_slug, &config, &usage); let composite = new_status_output( &config, &auth_manager, + Some(&token_info), &usage, &None, Some(&rate_display), @@ -646,9 +680,11 @@ async fn status_snapshot_shows_stale_limits_message() { let now = captured_at + ChronoDuration::minutes(20); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); + let token_info = token_info_for(&model_slug, &config, &usage); let composite = new_status_output( &config, &auth_manager, + Some(&token_info), &usage, &None, Some(&rate_display), @@ -708,9 +744,11 @@ async fn status_snapshot_cached_limits_hide_credits_without_flag() { let now = captured_at + ChronoDuration::minutes(20); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); + let token_info = token_info_for(&model_slug, &config, &usage); let composite = new_status_output( &config, &auth_manager, + Some(&token_info), &usage, &None, Some(&rate_display), @@ -727,3 +765,63 @@ async fn status_snapshot_cached_limits_hide_credits_without_flag() { let sanitized = sanitize_directory(rendered_lines).join("\n"); assert_snapshot!(sanitized); } + +#[tokio::test] +async fn status_context_window_uses_last_usage() { + let temp_home = TempDir::new().expect("temp home"); + let mut config = test_config(&temp_home).await; + config.model_context_window = Some(272_000); + + let auth_manager = test_auth_manager(&config); + let total_usage = TokenUsage { + input_tokens: 12_800, + cached_input_tokens: 0, + output_tokens: 879, + reasoning_output_tokens: 0, + total_tokens: 102_000, + }; + let last_usage = TokenUsage { + input_tokens: 12_800, + cached_input_tokens: 0, + output_tokens: 879, + reasoning_output_tokens: 0, + total_tokens: 13_679, + }; + + let now = chrono::Local + .with_ymd_and_hms(2024, 6, 1, 12, 0, 0) + .single() + .expect("timestamp"); + + let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); + let token_info = TokenUsageInfo { + total_token_usage: total_usage.clone(), + last_token_usage: last_usage.clone(), + model_context_window: config.model_context_window, + }; + let composite = new_status_output( + &config, + &auth_manager, + Some(&token_info), + &total_usage, + &None, + None, + None, + now, + &model_slug, + ); + let rendered_lines = render_lines(&composite.display_lines(80)); + let context_line = rendered_lines + .into_iter() + .find(|line| line.contains("Context window")) + .expect("context line"); + + assert!( + context_line.contains("13.7K used / 272K"), + "expected context line to reflect last usage tokens, got: {context_line}" + ); + assert!( + !context_line.contains("102K"), + "context line should not use total aggregated tokens, got: {context_line}" + ); +} From 1dad0bda2063fcc1935f0bfd4c6f65a377de62ae Mon Sep 17 00:00:00 2001 From: Ahmed Ibrahim Date: Thu, 1 Jan 2026 12:55:58 -0800 Subject: [PATCH 7/8] tui --- codex-rs/tui/src/chatwidget.rs | 10 ++-- codex-rs/tui/src/status/card.rs | 46 +++++++++++++++++ ...ched_limits_hide_credits_without_flag.snap | 35 ++++++------- ..._snapshot_includes_credits_and_limits.snap | 35 ++++++------- ...tatus_snapshot_includes_monthly_limit.snap | 31 +++++------ ...s_snapshot_includes_reasoning_details.snap | 33 ++++++------ ...s_snapshot_shows_empty_limits_message.snap | 31 +++++------ ...snapshot_shows_missing_limits_message.snap | 31 +++++------ ...s_snapshot_shows_stale_limits_message.snap | 35 ++++++------- ...snapshot_truncates_in_narrow_terminal.snap | 13 ++--- codex-rs/tui/src/status/tests.rs | 51 +++++++++++++++++++ 11 files changed, 228 insertions(+), 123 deletions(-) diff --git a/codex-rs/tui/src/chatwidget.rs b/codex-rs/tui/src/chatwidget.rs index 01495334929..3342cd11ee6 100644 --- a/codex-rs/tui/src/chatwidget.rs +++ b/codex-rs/tui/src/chatwidget.rs @@ -2239,14 +2239,14 @@ impl ChatWidget { pub(crate) fn add_status_output(&mut self) { let default_usage = TokenUsage::default(); - let total_usage = if let Some(ti) = &self.token_info { - &ti.total_token_usage - } else { - &default_usage - }; + let token_info = self.token_info.as_ref(); + let total_usage = token_info + .map(|ti| &ti.total_token_usage) + .unwrap_or(&default_usage); self.add_to_history(crate::status::new_status_output( &self.config, self.auth_manager.as_ref(), + token_info, total_usage, &self.conversation_id, self.rate_limit_snapshot.as_ref(), diff --git a/codex-rs/tui/src/status/card.rs b/codex-rs/tui/src/status/card.rs index b8875b4c305..07cd5a19888 100644 --- a/codex-rs/tui/src/status/card.rs +++ b/codex-rs/tui/src/status/card.rs @@ -10,6 +10,7 @@ use codex_core::config::Config; use codex_core::protocol::NetworkAccess; use codex_core::protocol::SandboxPolicy; use codex_core::protocol::TokenUsage; +use codex_core::protocol::TokenUsageInfo; use codex_protocol::ConversationId; use codex_protocol::account::PlanType; use ratatui::prelude::*; @@ -38,11 +39,19 @@ use crate::wrapping::RtOptions; use crate::wrapping::word_wrap_lines; use codex_core::AuthManager; +#[derive(Debug, Clone)] +struct StatusContextWindowData { + percent_remaining: i64, + tokens_in_context: i64, + window: i64, +} + #[derive(Debug, Clone)] pub(crate) struct StatusTokenUsageData { total: i64, input: i64, output: i64, + context_window: Option, } #[derive(Debug)] @@ -63,6 +72,7 @@ struct StatusHistoryCell { pub(crate) fn new_status_output( config: &Config, auth_manager: &AuthManager, + token_info: Option<&TokenUsageInfo>, total_usage: &TokenUsage, session_id: &Option, rate_limits: Option<&RateLimitSnapshotDisplay>, @@ -74,6 +84,7 @@ pub(crate) fn new_status_output( let card = StatusHistoryCell::new( config, auth_manager, + token_info, total_usage, session_id, rate_limits, @@ -90,6 +101,7 @@ impl StatusHistoryCell { fn new( config: &Config, auth_manager: &AuthManager, + token_info: Option<&TokenUsageInfo>, total_usage: &TokenUsage, session_id: &Option, rate_limits: Option<&RateLimitSnapshotDisplay>, @@ -119,11 +131,22 @@ impl StatusHistoryCell { let agents_summary = compose_agents_summary(config); let account = compose_account_display(auth_manager, plan_type); let session_id = session_id.as_ref().map(std::string::ToString::to_string); + let default_usage = TokenUsage::default(); + let (context_usage, context_window) = match token_info { + Some(info) => (&info.last_token_usage, info.model_context_window), + None => (&default_usage, config.model_context_window), + }; + let context_window = context_window.map(|window| StatusContextWindowData { + percent_remaining: context_usage.percent_of_context_window_remaining(window), + tokens_in_context: context_usage.tokens_in_context_window(), + window, + }); let token_usage = StatusTokenUsageData { total: total_usage.blended_total(), input: total_usage.non_cached_input(), output: total_usage.output_tokens, + context_window, }; let rate_limits = compose_rate_limit_data(rate_limits, now); @@ -159,6 +182,22 @@ impl StatusHistoryCell { ] } + fn context_window_spans(&self) -> Option>> { + let context = self.token_usage.context_window.as_ref()?; + let percent = context.percent_remaining; + let used_fmt = format_tokens_compact(context.tokens_in_context); + let window_fmt = format_tokens_compact(context.window); + + Some(vec![ + Span::from(format!("{percent}% left")), + Span::from(" (").dim(), + Span::from(used_fmt).dim(), + Span::from(" used / ").dim(), + Span::from(window_fmt).dim(), + Span::from(")").dim(), + ]) + } + fn rate_limit_lines( &self, available_inner_width: usize, @@ -306,6 +345,9 @@ impl HistoryCell for StatusHistoryCell { push_label(&mut labels, &mut seen, "Session"); } push_label(&mut labels, &mut seen, "Token usage"); + if self.token_usage.context_window.is_some() { + push_label(&mut labels, &mut seen, "Context window"); + } self.collect_rate_limit_labels(&mut seen, &mut labels); @@ -358,6 +400,10 @@ impl HistoryCell for StatusHistoryCell { lines.push(formatter.line("Token usage", self.token_usage_spans())); } + if let Some(spans) = self.context_window_spans() { + lines.push(formatter.line("Context window", spans)); + } + lines.extend(self.rate_limit_lines(available_inner_width, &formatter)); let content_width = lines.iter().map(line_display_width).max().unwrap_or(0); diff --git a/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_cached_limits_hide_credits_without_flag.snap b/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_cached_limits_hide_credits_without_flag.snap index 949b034516f..dbb634bab1c 100644 --- a/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_cached_limits_hide_credits_without_flag.snap +++ b/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_cached_limits_hide_credits_without_flag.snap @@ -4,20 +4,21 @@ expression: sanitized --- /status -╭───────────────────────────────────────────────────────────────────╮ -│ >_ OpenAI Codex (v0.0.0) │ -│ │ -│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ -│ information on rate limits and credits │ -│ │ -│ Model: gpt-5.1-codex (reasoning none, summaries auto) │ -│ Directory: [[workspace]] │ -│ Approval: on-request │ -│ Sandbox: read-only │ -│ Agents.md: │ -│ │ -│ Token usage: 1.05K total (700 input + 350 output) │ -│ 5h limit: [████████░░░░░░░░░░░░] 40% left (resets 11:32) │ -│ Weekly limit: [█████████████░░░░░░░] 65% left (resets 11:52) │ -│ Warning: limits may be stale - start new turn to refresh. │ -╰───────────────────────────────────────────────────────────────────╯ +╭─────────────────────────────────────────────────────────────────────╮ +│ >_ OpenAI Codex (v0.0.0) │ +│ │ +│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ +│ information on rate limits and credits │ +│ │ +│ Model: gpt-5.1-codex (reasoning none, summaries auto) │ +│ Directory: [[workspace]] │ +│ Approval: on-request │ +│ Sandbox: read-only │ +│ Agents.md: │ +│ │ +│ Token usage: 1.05K total (700 input + 350 output) │ +│ Context window: 100% left (1.45K used / 272K) │ +│ 5h limit: [████████░░░░░░░░░░░░] 40% left (resets 11:32) │ +│ Weekly limit: [█████████████░░░░░░░] 65% left (resets 11:52) │ +│ Warning: limits may be stale - start new turn to refresh. │ +╰─────────────────────────────────────────────────────────────────────╯ diff --git a/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_includes_credits_and_limits.snap b/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_includes_credits_and_limits.snap index 9afa9f8cf9f..1707a4c5fbc 100644 --- a/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_includes_credits_and_limits.snap +++ b/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_includes_credits_and_limits.snap @@ -4,20 +4,21 @@ expression: sanitized --- /status -╭─────────────────────────────────────────────────────────────────╮ -│ >_ OpenAI Codex (v0.0.0) │ -│ │ -│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ -│ information on rate limits and credits │ -│ │ -│ Model: gpt-5.1-codex (reasoning none, summaries auto) │ -│ Directory: [[workspace]] │ -│ Approval: on-request │ -│ Sandbox: read-only │ -│ Agents.md: │ -│ │ -│ Token usage: 2K total (1.4K input + 600 output) │ -│ 5h limit: [███████████░░░░░░░░░] 55% left (resets 09:25) │ -│ Weekly limit: [██████████████░░░░░░] 70% left (resets 09:55) │ -│ Credits: 38 credits │ -╰─────────────────────────────────────────────────────────────────╯ +╭───────────────────────────────────────────────────────────────────╮ +│ >_ OpenAI Codex (v0.0.0) │ +│ │ +│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ +│ information on rate limits and credits │ +│ │ +│ Model: gpt-5.1-codex (reasoning none, summaries auto) │ +│ Directory: [[workspace]] │ +│ Approval: on-request │ +│ Sandbox: read-only │ +│ Agents.md: │ +│ │ +│ Token usage: 2K total (1.4K input + 600 output) │ +│ Context window: 100% left (2.2K used / 272K) │ +│ 5h limit: [███████████░░░░░░░░░] 55% left (resets 09:25) │ +│ Weekly limit: [██████████████░░░░░░] 70% left (resets 09:55) │ +│ Credits: 38 credits │ +╰───────────────────────────────────────────────────────────────────╯ diff --git a/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_includes_monthly_limit.snap b/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_includes_monthly_limit.snap index 2d35bb69042..3ecc4fa8ed2 100644 --- a/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_includes_monthly_limit.snap +++ b/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_includes_monthly_limit.snap @@ -4,18 +4,19 @@ expression: sanitized --- /status -╭───────────────────────────────────────────────────────────────────────────╮ -│ >_ OpenAI Codex (v0.0.0) │ -│ │ -│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ -│ information on rate limits and credits │ -│ │ -│ Model: gpt-5.1-codex-max (reasoning none, summaries auto) │ -│ Directory: [[workspace]] │ -│ Approval: on-request │ -│ Sandbox: read-only │ -│ Agents.md: │ -│ │ -│ Token usage: 1.2K total (800 input + 400 output) │ -│ Monthly limit: [██████████████████░░] 88% left (resets 07:08 on 7 May) │ -╰───────────────────────────────────────────────────────────────────────────╯ +╭────────────────────────────────────────────────────────────────────────────╮ +│ >_ OpenAI Codex (v0.0.0) │ +│ │ +│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ +│ information on rate limits and credits │ +│ │ +│ Model: gpt-5.1-codex-max (reasoning none, summaries auto) │ +│ Directory: [[workspace]] │ +│ Approval: on-request │ +│ Sandbox: read-only │ +│ Agents.md: │ +│ │ +│ Token usage: 1.2K total (800 input + 400 output) │ +│ Context window: 100% left (1.2K used / 272K) │ +│ Monthly limit: [██████████████████░░] 88% left (resets 07:08 on 7 May) │ +╰────────────────────────────────────────────────────────────────────────────╯ diff --git a/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_includes_reasoning_details.snap b/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_includes_reasoning_details.snap index 604b463128d..c22577407ee 100644 --- a/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_includes_reasoning_details.snap +++ b/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_includes_reasoning_details.snap @@ -4,19 +4,20 @@ expression: sanitized --- /status -╭─────────────────────────────────────────────────────────────────────────╮ -│ >_ OpenAI Codex (v0.0.0) │ -│ │ -│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ -│ information on rate limits and credits │ -│ │ -│ Model: gpt-5.1-codex-max (reasoning high, summaries detailed) │ -│ Directory: [[workspace]] │ -│ Approval: on-request │ -│ Sandbox: workspace-write │ -│ Agents.md: │ -│ │ -│ Token usage: 1.9K total (1K input + 900 output) │ -│ 5h limit: [██████░░░░░░░░░░░░░░] 28% left (resets 03:14) │ -│ Weekly limit: [███████████░░░░░░░░░] 55% left (resets 03:24) │ -╰─────────────────────────────────────────────────────────────────────────╯ +╭───────────────────────────────────────────────────────────────────────────╮ +│ >_ OpenAI Codex (v0.0.0) │ +│ │ +│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ +│ information on rate limits and credits │ +│ │ +│ Model: gpt-5.1-codex-max (reasoning high, summaries detailed) │ +│ Directory: [[workspace]] │ +│ Approval: on-request │ +│ Sandbox: workspace-write │ +│ Agents.md: │ +│ │ +│ Token usage: 1.9K total (1K input + 900 output) │ +│ Context window: 100% left (2.25K used / 272K) │ +│ 5h limit: [██████░░░░░░░░░░░░░░] 28% left (resets 03:14) │ +│ Weekly limit: [███████████░░░░░░░░░] 55% left (resets 03:24) │ +╰───────────────────────────────────────────────────────────────────────────╯ diff --git a/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_shows_empty_limits_message.snap b/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_shows_empty_limits_message.snap index ce58f59a288..f0e6b734454 100644 --- a/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_shows_empty_limits_message.snap +++ b/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_shows_empty_limits_message.snap @@ -4,18 +4,19 @@ expression: sanitized --- /status -╭────────────────────────────────────────────────────────────────────╮ -│ >_ OpenAI Codex (v0.0.0) │ -│ │ -│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ -│ information on rate limits and credits │ -│ │ -│ Model: gpt-5.1-codex-max (reasoning none, summaries auto) │ -│ Directory: [[workspace]] │ -│ Approval: on-request │ -│ Sandbox: read-only │ -│ Agents.md: │ -│ │ -│ Token usage: 750 total (500 input + 250 output) │ -│ Limits: data not available yet │ -╰────────────────────────────────────────────────────────────────────╯ +╭───────────────────────────────────────────────────────────────────────╮ +│ >_ OpenAI Codex (v0.0.0) │ +│ │ +│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ +│ information on rate limits and credits │ +│ │ +│ Model: gpt-5.1-codex-max (reasoning none, summaries auto) │ +│ Directory: [[workspace]] │ +│ Approval: on-request │ +│ Sandbox: read-only │ +│ Agents.md: │ +│ │ +│ Token usage: 750 total (500 input + 250 output) │ +│ Context window: 100% left (750 used / 272K) │ +│ Limits: data not available yet │ +╰───────────────────────────────────────────────────────────────────────╯ diff --git a/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_shows_missing_limits_message.snap b/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_shows_missing_limits_message.snap index ce58f59a288..f0e6b734454 100644 --- a/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_shows_missing_limits_message.snap +++ b/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_shows_missing_limits_message.snap @@ -4,18 +4,19 @@ expression: sanitized --- /status -╭────────────────────────────────────────────────────────────────────╮ -│ >_ OpenAI Codex (v0.0.0) │ -│ │ -│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ -│ information on rate limits and credits │ -│ │ -│ Model: gpt-5.1-codex-max (reasoning none, summaries auto) │ -│ Directory: [[workspace]] │ -│ Approval: on-request │ -│ Sandbox: read-only │ -│ Agents.md: │ -│ │ -│ Token usage: 750 total (500 input + 250 output) │ -│ Limits: data not available yet │ -╰────────────────────────────────────────────────────────────────────╯ +╭───────────────────────────────────────────────────────────────────────╮ +│ >_ OpenAI Codex (v0.0.0) │ +│ │ +│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ +│ information on rate limits and credits │ +│ │ +│ Model: gpt-5.1-codex-max (reasoning none, summaries auto) │ +│ Directory: [[workspace]] │ +│ Approval: on-request │ +│ Sandbox: read-only │ +│ Agents.md: │ +│ │ +│ Token usage: 750 total (500 input + 250 output) │ +│ Context window: 100% left (750 used / 272K) │ +│ Limits: data not available yet │ +╰───────────────────────────────────────────────────────────────────────╯ diff --git a/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_shows_stale_limits_message.snap b/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_shows_stale_limits_message.snap index 2c20713a322..a12be950bcc 100644 --- a/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_shows_stale_limits_message.snap +++ b/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_shows_stale_limits_message.snap @@ -4,20 +4,21 @@ expression: sanitized --- /status -╭─────────────────────────────────────────────────────────────────────╮ -│ >_ OpenAI Codex (v0.0.0) │ -│ │ -│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ -│ information on rate limits and credits │ -│ │ -│ Model: gpt-5.1-codex-max (reasoning none, summaries auto) │ -│ Directory: [[workspace]] │ -│ Approval: on-request │ -│ Sandbox: read-only │ -│ Agents.md: │ -│ │ -│ Token usage: 1.9K total (1K input + 900 output) │ -│ 5h limit: [██████░░░░░░░░░░░░░░] 28% left (resets 03:14) │ -│ Weekly limit: [████████████░░░░░░░░] 60% left (resets 03:34) │ -│ Warning: limits may be stale - start new turn to refresh. │ -╰─────────────────────────────────────────────────────────────────────╯ +╭───────────────────────────────────────────────────────────────────────╮ +│ >_ OpenAI Codex (v0.0.0) │ +│ │ +│ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ +│ information on rate limits and credits │ +│ │ +│ Model: gpt-5.1-codex-max (reasoning none, summaries auto) │ +│ Directory: [[workspace]] │ +│ Approval: on-request │ +│ Sandbox: read-only │ +│ Agents.md: │ +│ │ +│ Token usage: 1.9K total (1K input + 900 output) │ +│ Context window: 100% left (2.25K used / 272K) │ +│ 5h limit: [██████░░░░░░░░░░░░░░] 28% left (resets 03:14) │ +│ Weekly limit: [████████████░░░░░░░░] 60% left (resets 03:34) │ +│ Warning: limits may be stale - start new turn to refresh. │ +╰───────────────────────────────────────────────────────────────────────╯ diff --git a/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_truncates_in_narrow_terminal.snap b/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_truncates_in_narrow_terminal.snap index 2d43cda3eca..02ba1adec91 100644 --- a/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_truncates_in_narrow_terminal.snap +++ b/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_truncates_in_narrow_terminal.snap @@ -10,12 +10,13 @@ expression: sanitized │ Visit https://chatgpt.com/codex/settings/usage for up-to-date │ │ information on rate limits and credits │ │ │ -│ Model: gpt-5.1-codex-max (reasoning high, summaries detai │ +│ Model: gpt-5.1-codex-max (reasoning high, summaries de │ │ Directory: [[workspace]] │ -│ Approval: on-request │ -│ Sandbox: read-only │ -│ Agents.md: │ +│ Approval: on-request │ +│ Sandbox: read-only │ +│ Agents.md: │ │ │ -│ Token usage: 1.9K total (1K input + 900 output) │ -│ 5h limit: [██████░░░░░░░░░░░░░░] 28% left (resets 03:14) │ +│ Token usage: 1.9K total (1K input + 900 output) │ +│ Context window: 100% left (2.25K used / 272K) │ +│ 5h limit: [██████░░░░░░░░░░░░░░] 28% left (resets 03:14) │ ╰────────────────────────────────────────────────────────────────────╯ diff --git a/codex-rs/tui/src/status/tests.rs b/codex-rs/tui/src/status/tests.rs index e1a134e74be..8e1acd99dc1 100644 --- a/codex-rs/tui/src/status/tests.rs +++ b/codex-rs/tui/src/status/tests.rs @@ -13,6 +13,7 @@ use codex_core::protocol::RateLimitSnapshot; use codex_core::protocol::RateLimitWindow; use codex_core::protocol::SandboxPolicy; use codex_core::protocol::TokenUsage; +use codex_core::protocol::TokenUsageInfo; use codex_protocol::config_types::ReasoningSummary; use codex_protocol::openai_models::ReasoningEffort; use insta::assert_snapshot; @@ -36,6 +37,17 @@ fn test_auth_manager(config: &Config) -> AuthManager { ) } +fn token_info_for(model_slug: &str, config: &Config, usage: &TokenUsage) -> TokenUsageInfo { + let context_window = ModelsManager::construct_model_family_offline(model_slug, config) + .context_window + .or(config.model_context_window); + TokenUsageInfo { + total_token_usage: usage.clone(), + last_token_usage: usage.clone(), + model_context_window: context_window, + } +} + fn render_lines(lines: &[Line<'static>]) -> Vec { lines .iter() @@ -127,10 +139,12 @@ async fn status_snapshot_includes_reasoning_details() { let rate_display = rate_limit_snapshot_display(&snapshot, captured_at); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); + let token_info = token_info_for(&model_slug, &config, &usage); let composite = new_status_output( &config, &auth_manager, + Some(&token_info), &usage, &None, Some(&rate_display), @@ -182,9 +196,11 @@ async fn status_snapshot_includes_monthly_limit() { let rate_display = rate_limit_snapshot_display(&snapshot, captured_at); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); + let token_info = token_info_for(&model_slug, &config, &usage); let composite = new_status_output( &config, &auth_manager, + Some(&token_info), &usage, &None, Some(&rate_display), @@ -224,9 +240,11 @@ async fn status_snapshot_shows_unlimited_credits() { }; let rate_display = rate_limit_snapshot_display(&snapshot, captured_at); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); + let token_info = token_info_for(&model_slug, &config, &usage); let composite = new_status_output( &config, &auth_manager, + Some(&token_info), &usage, &None, Some(&rate_display), @@ -265,9 +283,11 @@ async fn status_snapshot_shows_positive_credits() { }; let rate_display = rate_limit_snapshot_display(&snapshot, captured_at); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); + let token_info = token_info_for(&model_slug, &config, &usage); let composite = new_status_output( &config, &auth_manager, + Some(&token_info), &usage, &None, Some(&rate_display), @@ -306,9 +326,11 @@ async fn status_snapshot_hides_zero_credits() { }; let rate_display = rate_limit_snapshot_display(&snapshot, captured_at); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); + let token_info = token_info_for(&model_slug, &config, &usage); let composite = new_status_output( &config, &auth_manager, + Some(&token_info), &usage, &None, Some(&rate_display), @@ -345,9 +367,11 @@ async fn status_snapshot_hides_when_has_no_credits_flag() { }; let rate_display = rate_limit_snapshot_display(&snapshot, captured_at); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); + let token_info = token_info_for(&model_slug, &config, &usage); let composite = new_status_output( &config, &auth_manager, + Some(&token_info), &usage, &None, Some(&rate_display), @@ -384,9 +408,11 @@ async fn status_card_token_usage_excludes_cached_tokens() { .expect("timestamp"); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); + let token_info = token_info_for(&model_slug, &config, &usage); let composite = new_status_output( &config, &auth_manager, + Some(&token_info), &usage, &None, None, @@ -438,9 +464,11 @@ async fn status_snapshot_truncates_in_narrow_terminal() { let rate_display = rate_limit_snapshot_display(&snapshot, captured_at); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); + let token_info = token_info_for(&model_slug, &config, &usage); let composite = new_status_output( &config, &auth_manager, + Some(&token_info), &usage, &None, Some(&rate_display), @@ -481,9 +509,11 @@ async fn status_snapshot_shows_missing_limits_message() { .expect("timestamp"); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); + let token_info = token_info_for(&model_slug, &config, &usage); let composite = new_status_output( &config, &auth_manager, + Some(&token_info), &usage, &None, None, @@ -542,9 +572,11 @@ async fn status_snapshot_includes_credits_and_limits() { let rate_display = rate_limit_snapshot_display(&snapshot, captured_at); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); + let token_info = token_info_for(&model_slug, &config, &usage); let composite = new_status_output( &config, &auth_manager, + Some(&token_info), &usage, &None, Some(&rate_display), @@ -591,9 +623,11 @@ async fn status_snapshot_shows_empty_limits_message() { let rate_display = rate_limit_snapshot_display(&snapshot, captured_at); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); + let token_info = token_info_for(&model_slug, &config, &usage); let composite = new_status_output( &config, &auth_manager, + Some(&token_info), &usage, &None, Some(&rate_display), @@ -649,9 +683,11 @@ async fn status_snapshot_shows_stale_limits_message() { let now = captured_at + ChronoDuration::minutes(20); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); + let token_info = token_info_for(&model_slug, &config, &usage); let composite = new_status_output( &config, &auth_manager, + Some(&token_info), &usage, &None, Some(&rate_display), @@ -711,9 +747,11 @@ async fn status_snapshot_cached_limits_hide_credits_without_flag() { let now = captured_at + ChronoDuration::minutes(20); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); + let token_info = token_info_for(&model_slug, &config, &usage); let composite = new_status_output( &config, &auth_manager, + Some(&token_info), &usage, &None, Some(&rate_display), @@ -745,6 +783,13 @@ async fn status_context_window_uses_last_usage() { reasoning_output_tokens: 0, total_tokens: 102_000, }; + let last_usage = TokenUsage { + input_tokens: 12_800, + cached_input_tokens: 0, + output_tokens: 879, + reasoning_output_tokens: 0, + total_tokens: 13_679, + }; let now = chrono::Local .with_ymd_and_hms(2024, 6, 1, 12, 0, 0) @@ -752,9 +797,15 @@ async fn status_context_window_uses_last_usage() { .expect("timestamp"); let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); + let token_info = TokenUsageInfo { + total_token_usage: total_usage.clone(), + last_token_usage: last_usage.clone(), + model_context_window: config.model_context_window, + }; let composite = new_status_output( &config, &auth_manager, + Some(&token_info), &total_usage, &None, None, From 08c3eb4d11cc25d293a5b799f7d94568de8b60e7 Mon Sep 17 00:00:00 2001 From: Ahmed Ibrahim Date: Fri, 2 Jan 2026 09:56:02 -0800 Subject: [PATCH 8/8] cline --- codex-rs/tui/src/status/tests.rs | 2 +- codex-rs/tui2/src/status/tests.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/codex-rs/tui/src/status/tests.rs b/codex-rs/tui/src/status/tests.rs index 8e1acd99dc1..c6f6c735995 100644 --- a/codex-rs/tui/src/status/tests.rs +++ b/codex-rs/tui/src/status/tests.rs @@ -799,7 +799,7 @@ async fn status_context_window_uses_last_usage() { let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); let token_info = TokenUsageInfo { total_token_usage: total_usage.clone(), - last_token_usage: last_usage.clone(), + last_token_usage: last_usage, model_context_window: config.model_context_window, }; let composite = new_status_output( diff --git a/codex-rs/tui2/src/status/tests.rs b/codex-rs/tui2/src/status/tests.rs index bae9dfdbd15..7eb18dd48bb 100644 --- a/codex-rs/tui2/src/status/tests.rs +++ b/codex-rs/tui2/src/status/tests.rs @@ -796,7 +796,7 @@ async fn status_context_window_uses_last_usage() { let model_slug = ModelsManager::get_model_offline(config.model.as_deref()); let token_info = TokenUsageInfo { total_token_usage: total_usage.clone(), - last_token_usage: last_usage.clone(), + last_token_usage: last_usage, model_context_window: config.model_context_window, }; let composite = new_status_output(