Skip to content

Commit e743d25

Browse files
authored
Add opt-out for rate limit model nudge (#6433)
## Summary - add a `hide_rate_limit_model_nudge` notice flag plus config edit plumbing so the rate limit reminder preference is persisted and documented - extend the chat widget prompt with a "never show again" option, and wire new app events so selecting it hides future nudges immediately and writes the config - add unit coverage and refresh the snapshot for the three-option prompt ## Testing - `just fmt` - `just fix -p codex-tui` - `just fix -p codex-core` - `cargo test -p codex-tui` - `cargo test -p codex-core` *(fails at `exec::tests::kill_child_process_group_kills_grandchildren_on_timeout`: grandchild process still alive)* ------ [Codex Task](https://chatgpt.com/codex/tasks/task_i_6910d7f407748321b2661fc355416994)
1 parent 788badd commit e743d25

File tree

8 files changed

+123
-2
lines changed

8 files changed

+123
-2
lines changed

codex-rs/core/src/config/edit.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ pub enum ConfigEdit {
2525
SetNoticeHideFullAccessWarning(bool),
2626
/// Toggle the Windows world-writable directories warning acknowledgement flag.
2727
SetNoticeHideWorldWritableWarning(bool),
28+
/// Toggle the rate limit model nudge acknowledgement flag.
29+
SetNoticeHideRateLimitModelNudge(bool),
2830
/// Toggle the Windows onboarding acknowledgement flag.
2931
SetWindowsWslSetupAcknowledged(bool),
3032
/// Replace the entire `[mcp_servers]` table.
@@ -246,6 +248,11 @@ impl ConfigDocument {
246248
&[Notice::TABLE_KEY, "hide_world_writable_warning"],
247249
value(*acknowledged),
248250
)),
251+
ConfigEdit::SetNoticeHideRateLimitModelNudge(acknowledged) => Ok(self.write_value(
252+
Scope::Global,
253+
&[Notice::TABLE_KEY, "hide_rate_limit_model_nudge"],
254+
value(*acknowledged),
255+
)),
249256
ConfigEdit::SetWindowsWslSetupAcknowledged(acknowledged) => Ok(self.write_value(
250257
Scope::Global,
251258
&["windows_wsl_setup_acknowledged"],
@@ -486,6 +493,12 @@ impl ConfigEditsBuilder {
486493
self
487494
}
488495

496+
pub fn set_hide_rate_limit_model_nudge(mut self, acknowledged: bool) -> Self {
497+
self.edits
498+
.push(ConfigEdit::SetNoticeHideRateLimitModelNudge(acknowledged));
499+
self
500+
}
501+
489502
pub fn set_windows_wsl_setup_acknowledged(mut self, acknowledged: bool) -> Self {
490503
self.edits
491504
.push(ConfigEdit::SetWindowsWslSetupAcknowledged(acknowledged));
@@ -733,6 +746,34 @@ hide_full_access_warning = true
733746
assert_eq!(contents, expected);
734747
}
735748

749+
#[test]
750+
fn blocking_set_hide_rate_limit_model_nudge_preserves_table() {
751+
let tmp = tempdir().expect("tmpdir");
752+
let codex_home = tmp.path();
753+
std::fs::write(
754+
codex_home.join(CONFIG_TOML_FILE),
755+
r#"[notice]
756+
existing = "value"
757+
"#,
758+
)
759+
.expect("seed");
760+
761+
apply_blocking(
762+
codex_home,
763+
None,
764+
&[ConfigEdit::SetNoticeHideRateLimitModelNudge(true)],
765+
)
766+
.expect("persist");
767+
768+
let contents =
769+
std::fs::read_to_string(codex_home.join(CONFIG_TOML_FILE)).expect("read config");
770+
let expected = r#"[notice]
771+
existing = "value"
772+
hide_rate_limit_model_nudge = true
773+
"#;
774+
assert_eq!(contents, expected);
775+
}
776+
736777
#[test]
737778
fn blocking_replace_mcp_servers_round_trips() {
738779
let tmp = tempdir().expect("tmpdir");

codex-rs/core/src/config/types.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,8 @@ pub struct Notice {
360360
pub hide_full_access_warning: Option<bool>,
361361
/// Tracks whether the user has acknowledged the Windows world-writable directories warning.
362362
pub hide_world_writable_warning: Option<bool>,
363+
/// Tracks whether the user opted out of the rate limit model switch reminder.
364+
pub hide_rate_limit_model_nudge: Option<bool>,
363365
}
364366

365367
impl Notice {

codex-rs/tui/src/app.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,9 @@ impl App {
499499
self.chat_widget
500500
.set_world_writable_warning_acknowledged(ack);
501501
}
502+
AppEvent::UpdateRateLimitSwitchPromptHidden(hidden) => {
503+
self.chat_widget.set_rate_limit_switch_prompt_hidden(hidden);
504+
}
502505
AppEvent::PersistFullAccessWarningAcknowledged => {
503506
if let Err(err) = ConfigEditsBuilder::new(&self.config.codex_home)
504507
.set_hide_full_access_warning(true)
@@ -529,6 +532,21 @@ impl App {
529532
));
530533
}
531534
}
535+
AppEvent::PersistRateLimitSwitchPromptHidden => {
536+
if let Err(err) = ConfigEditsBuilder::new(&self.config.codex_home)
537+
.set_hide_rate_limit_model_nudge(true)
538+
.apply()
539+
.await
540+
{
541+
tracing::error!(
542+
error = %err,
543+
"failed to persist rate limit switch prompt preference"
544+
);
545+
self.chat_widget.add_error_message(format!(
546+
"Failed to save rate limit reminder preference: {err}"
547+
));
548+
}
549+
}
532550
AppEvent::OpenApprovalsPopup => {
533551
self.chat_widget.open_approvals_popup();
534552
}

codex-rs/tui/src/app_event.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,13 +104,19 @@ pub(crate) enum AppEvent {
104104
#[cfg_attr(not(target_os = "windows"), allow(dead_code))]
105105
UpdateWorldWritableWarningAcknowledged(bool),
106106

107+
/// Update whether the rate limit switch prompt has been acknowledged for the session.
108+
UpdateRateLimitSwitchPromptHidden(bool),
109+
107110
/// Persist the acknowledgement flag for the full access warning prompt.
108111
PersistFullAccessWarningAcknowledged,
109112

110113
/// Persist the acknowledgement flag for the world-writable directories warning.
111114
#[cfg_attr(not(target_os = "windows"), allow(dead_code))]
112115
PersistWorldWritableWarningAcknowledged,
113116

117+
/// Persist the acknowledgement flag for the rate limit switch prompt.
118+
PersistRateLimitSwitchPromptHidden,
119+
114120
/// Skip the next world-writable scan (one-shot) after a user-confirmed continue.
115121
#[cfg_attr(not(target_os = "windows"), allow(dead_code))]
116122
SkipNextWorldWritableScan,

codex-rs/tui/src/chatwidget.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -522,6 +522,7 @@ impl ChatWidget {
522522
.unwrap_or(false);
523523

524524
if high_usage
525+
&& !self.rate_limit_switch_prompt_hidden()
525526
&& self.config.model != NUDGE_MODEL_SLUG
526527
&& !matches!(
527528
self.rate_limit_switch_prompt,
@@ -1710,7 +1711,18 @@ impl ChatWidget {
17101711
.find(|preset| preset.model == NUDGE_MODEL_SLUG)
17111712
}
17121713

1714+
fn rate_limit_switch_prompt_hidden(&self) -> bool {
1715+
self.config
1716+
.notices
1717+
.hide_rate_limit_model_nudge
1718+
.unwrap_or(false)
1719+
}
1720+
17131721
fn maybe_show_pending_rate_limit_prompt(&mut self) {
1722+
if self.rate_limit_switch_prompt_hidden() {
1723+
self.rate_limit_switch_prompt = RateLimitSwitchPromptState::Idle;
1724+
return;
1725+
}
17141726
if !matches!(
17151727
self.rate_limit_switch_prompt,
17161728
RateLimitSwitchPromptState::Pending
@@ -1744,6 +1756,10 @@ impl ChatWidget {
17441756
})];
17451757

17461758
let keep_actions: Vec<SelectionAction> = Vec::new();
1759+
let never_actions: Vec<SelectionAction> = vec![Box::new(|tx| {
1760+
tx.send(AppEvent::UpdateRateLimitSwitchPromptHidden(true));
1761+
tx.send(AppEvent::PersistRateLimitSwitchPromptHidden);
1762+
})];
17471763
let description = if preset.description.is_empty() {
17481764
Some("Uses fewer credits for upcoming turns.".to_string())
17491765
} else {
@@ -1769,6 +1785,17 @@ impl ChatWidget {
17691785
dismiss_on_select: true,
17701786
..Default::default()
17711787
},
1788+
SelectionItem {
1789+
name: "Keep current model (never show again)".to_string(),
1790+
description: Some(
1791+
"Hide future rate limit reminders about switching models.".to_string(),
1792+
),
1793+
selected_description: None,
1794+
is_current: false,
1795+
actions: never_actions,
1796+
dismiss_on_select: true,
1797+
..Default::default()
1798+
},
17721799
];
17731800

17741801
self.bottom_pane.show_selection_view(SelectionViewParams {
@@ -2386,6 +2413,13 @@ impl ChatWidget {
23862413
self.config.notices.hide_world_writable_warning = Some(acknowledged);
23872414
}
23882415

2416+
pub(crate) fn set_rate_limit_switch_prompt_hidden(&mut self, hidden: bool) {
2417+
self.config.notices.hide_rate_limit_model_nudge = Some(hidden);
2418+
if hidden {
2419+
self.rate_limit_switch_prompt = RateLimitSwitchPromptState::Idle;
2420+
}
2421+
}
2422+
23892423
#[cfg_attr(not(target_os = "windows"), allow(dead_code))]
23902424
pub(crate) fn world_writable_warning_hidden(&self) -> bool {
23912425
self.config
Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
---
22
source: tui/src/chatwidget/tests.rs
3+
assertion_line: 500
34
expression: popup
45
---
56
Approaching rate limits
67
Switch to gpt-5-codex-mini for lower credit usage?
78

8-
1. Switch to gpt-5-codex-mini Optimized for codex. Cheaper, faster, but
9-
less capable.
9+
1. Switch to gpt-5-codex-mini Optimized for codex. Cheaper,
10+
faster, but less capable.
1011
2. Keep current model
12+
3. Keep current model (never show again) Hide future rate limit reminders
13+
about switching models.
1114

1215
Press enter to confirm or esc to go back

codex-rs/tui/src/chatwidget/tests.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,22 @@ fn rate_limit_switch_prompt_shows_once_per_session() {
448448
));
449449
}
450450

451+
#[test]
452+
fn rate_limit_switch_prompt_respects_hidden_notice() {
453+
let auth = CodexAuth::create_dummy_chatgpt_auth_for_testing();
454+
let (mut chat, _, _) = make_chatwidget_manual();
455+
chat.config.model = "gpt-5".to_string();
456+
chat.auth_manager = AuthManager::from_auth_for_testing(auth);
457+
chat.config.notices.hide_rate_limit_model_nudge = Some(true);
458+
459+
chat.on_rate_limit_snapshot(Some(snapshot(95.0)));
460+
461+
assert!(matches!(
462+
chat.rate_limit_switch_prompt,
463+
RateLimitSwitchPromptState::Idle
464+
));
465+
}
466+
451467
#[test]
452468
fn rate_limit_switch_prompt_defers_until_task_complete() {
453469
let auth = CodexAuth::create_dummy_chatgpt_auth_for_testing();

docs/example-config.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ windows_wsl_setup_acknowledged = false
159159
# In-product notices (mostly set automatically by Codex).
160160
[notice]
161161
# hide_full_access_warning = true
162+
# hide_rate_limit_model_nudge = true
162163

163164
################################################################################
164165
# Authentication & Login

0 commit comments

Comments
 (0)