Lowercase service tier names in the model status line (#60972)

## What changed

Render service tier names in lowercase for the `model-with-reasoning` status line item, while preserving their display casing in terminal titles.

## Testing

Extend footer snapshots to cover Fast and Ultrafast, verify terminal title casing, and check lowercase Flex and multiword tier labels.

GitOrigin-RevId: 16227735d4aae55463df537b91ff7e81c436a66e
This commit is contained in:
Benjamin Carlsson
2026-11-12 02:15:25 +00:00
committed by copyberry
parent f988ada4bf
commit 0702ebed4d
3 changed files with 76 additions and 24 deletions
@@ -0,0 +1,10 @@
---
source: tui/src/chatwidget/tests/status_and_layout.rs
expression: normalized_backend_snapshot(terminal.backend())
---
" "
" "
"› Ask Codex to do anything "
" "
" gpt-5.4 xhigh ultrafast · Context 0% used · /tmp/project "
" ? for shortcuts "
@@ -723,7 +723,10 @@ impl ChatWidget {
pub(super) fn status_line_value_for_item(&mut self, item: StatusLineItem) -> Option<String> {
match item {
StatusLineItem::ModelName => Some(self.model_display_name().to_string()),
StatusLineItem::ModelWithReasoning => Some(self.model_with_reasoning_display_name()),
StatusLineItem::ModelWithReasoning => {
let service_tier = self.processing_tier_display_name().map(str::to_lowercase);
Some(self.model_with_reasoning_display_name(service_tier.as_deref()))
}
StatusLineItem::Reasoning => Some(self.reasoning_display_name()),
StatusLineItem::Profile => {
let layer = self.config.config_layer_stack.get_active_user_layer()?;
@@ -970,7 +973,7 @@ impl ChatWidget {
/*max_chars*/ 32,
)),
TerminalTitleItem::ModelWithReasoning => Some(Self::truncate_terminal_title_part(
self.model_with_reasoning_display_name(),
self.model_with_reasoning_display_name(self.processing_tier_display_name()),
/*max_chars*/ 32,
)),
TerminalTitleItem::Reasoning => Some(Self::truncate_terminal_title_part(
@@ -986,10 +989,9 @@ impl ChatWidget {
Self::status_line_reasoning_effort_label(effort.as_ref())
}
fn model_with_reasoning_display_name(&self) -> String {
fn model_with_reasoning_display_name(&self, service_tier: Option<&str>) -> String {
let label = self.reasoning_display_name();
let service_tier_label = self
.processing_tier_display_name()
let service_tier_label = service_tier
.map(|tier| format!(" {tier}"))
.unwrap_or_default();
format!("{} {label}{service_tier_label}", self.model_display_name())
@@ -4506,13 +4506,8 @@ async fn renamed_thread_footer_title_snapshot() {
}
#[tokio::test]
async fn status_line_model_with_reasoning_fast_footer_snapshot() {
use ratatui::Terminal;
use ratatui::backend::TestBackend;
async fn status_line_model_with_reasoning_speed_footer_snapshots() {
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.4")).await;
set_fast_mode_test_catalog(&mut chat);
assert!(get_available_model(&chat, "gpt-5.4").supports_fast_mode());
chat.show_welcome_banner = false;
chat.config.cwd = test_project_path().abs();
chat.local_settings.tui.status_line = Some(vec![
@@ -4520,23 +4515,68 @@ async fn status_line_model_with_reasoning_fast_footer_snapshot() {
"context-used".to_string(),
"current-dir".to_string(),
]);
chat.local_settings.tui.terminal_title = Some(vec!["model-with-reasoning".to_string()]);
chat.set_reasoning_effort(Some(ReasoningEffortConfig::XHigh));
chat.set_service_tier(Some(ServiceTier::Fast.request_value().to_string()));
set_chatgpt_auth(&mut chat);
set_fast_mode_test_catalog(&mut chat);
assert!(get_available_model(&chat, "gpt-5.4").supports_fast_mode());
chat.refresh_status_line();
chat.set_feature_enabled(Feature::UltrafastMode, /*enabled*/ true);
let width = 80;
let height = chat.desired_height(width);
let mut terminal = Terminal::new(TestBackend::new(width, height)).expect("create terminal");
terminal
.draw(|f| chat.render(f.area(), f.buffer_mut()))
.expect("draw model-with-reasoning footer");
assert_chatwidget_snapshot!(
"status_line_model_with_reasoning_fast_footer",
normalized_backend_snapshot(terminal.backend())
);
let mut preset = get_available_model(&chat, "gpt-5.4");
let fast_tier = preset
.service_tiers
.iter_mut()
.find(|tier| tier.id == ServiceTier::Fast.request_value())
.expect("fast tier");
fast_tier.name = "Fast".to_string();
preset.service_tiers.extend([
codex_protocol::openai_models::ModelServiceTier {
id: "ultrafast".to_string(),
name: "Ultrafast".to_string(),
description: "Ultrafast processing".to_string(),
},
codex_protocol::openai_models::ModelServiceTier {
id: "low_latency".to_string(),
name: "Low Latency".to_string(),
description: String::new(),
},
]);
chat.model_catalog = Arc::new(ModelCatalog::new(vec![preset]));
for (tier_id, tier_name, snapshot) in [
(
ServiceTier::Fast.request_value(),
"Fast",
"status_line_model_with_reasoning_fast_footer",
),
(
"ultrafast",
"Ultrafast",
"status_line_model_with_reasoning_ultrafast_footer",
),
] {
chat.set_service_tier(Some(tier_id.to_string()));
chat.refresh_status_line();
chat.refresh_terminal_title();
assert_eq!(
chat.last_terminal_title.as_deref(),
Some(format!("gpt-5.4 xhigh {tier_name}").as_str())
);
let width = 80;
let height = chat.desired_height(width);
let mut terminal = Terminal::new(TestBackend::new(width, height)).expect("create terminal");
terminal
.draw(|f| chat.render(f.area(), f.buffer_mut()))
.expect("draw model-with-reasoning footer");
assert_chatwidget_snapshot!(snapshot, normalized_backend_snapshot(terminal.backend()));
}
chat.local_settings.tui.status_line = Some(vec!["model-with-reasoning".to_string()]);
chat.set_service_tier(Some(ServiceTier::Flex.request_value().to_string()));
insta::assert_snapshot!(status_line_text(&chat).expect("status line"), @"gpt-5.4 xhigh flex");
chat.set_service_tier(Some("low_latency".to_string()));
insta::assert_snapshot!(status_line_text(&chat).expect("status line"), @"gpt-5.4 xhigh low latency");
}
#[tokio::test]