Skip to content

Commit 021e39b

Browse files
authored
Show model/reasoning hint when switching modes (#12307)
## Summary - show an info message when switching collaboration modes changes the effective model or reasoning - include the target mode in the message (for example `... for Plan mode.`) - add TUI tests for model-change and reasoning-only change notifications on mode switch <img width="715" height="184" alt="Screenshot 2026-02-20 at 2 01 40 PM" src="https://github.com/user-attachments/assets/18d1beb3-ab87-4e1c-9ada-a10218520420" />
1 parent 65b9fe8 commit 021e39b

File tree

2 files changed

+93
-1
lines changed

2 files changed

+93
-1
lines changed

codex-rs/tui/src/chatwidget.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6343,9 +6343,36 @@ impl ChatWidget {
63436343
if !self.collaboration_modes_enabled() {
63446344
return;
63456345
}
6346+
let previous_mode = self.active_mode_kind();
6347+
let previous_model = self.current_model().to_string();
6348+
let previous_effort = self.effective_reasoning_effort();
63466349
self.active_collaboration_mask = Some(mask);
63476350
self.update_collaboration_mode_indicator();
63486351
self.refresh_model_display();
6352+
let next_mode = self.active_mode_kind();
6353+
let next_model = self.current_model();
6354+
let next_effort = self.effective_reasoning_effort();
6355+
if previous_mode != next_mode
6356+
&& (previous_model != next_model || previous_effort != next_effort)
6357+
{
6358+
let mut message = format!("Model changed to {next_model}");
6359+
if !next_model.starts_with("codex-auto-") {
6360+
let reasoning_label = match next_effort {
6361+
Some(ReasoningEffortConfig::Minimal) => "minimal",
6362+
Some(ReasoningEffortConfig::Low) => "low",
6363+
Some(ReasoningEffortConfig::Medium) => "medium",
6364+
Some(ReasoningEffortConfig::High) => "high",
6365+
Some(ReasoningEffortConfig::XHigh) => "xhigh",
6366+
None | Some(ReasoningEffortConfig::None) => "default",
6367+
};
6368+
message.push(' ');
6369+
message.push_str(reasoning_label);
6370+
}
6371+
message.push_str(" for ");
6372+
message.push_str(next_mode.display_name());
6373+
message.push_str(" mode.");
6374+
self.add_info_message(message, None);
6375+
}
63496376
self.request_redraw();
63506377
}
63516378

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

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3584,6 +3584,66 @@ async fn collab_mode_shift_tab_cycles_only_when_enabled_and_idle() {
35843584
assert_eq!(chat.active_collaboration_mode_kind(), before);
35853585
}
35863586

3587+
#[tokio::test]
3588+
async fn mode_switch_surfaces_model_change_notification_when_effective_model_changes() {
3589+
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5")).await;
3590+
chat.set_feature_enabled(Feature::CollaborationModes, true);
3591+
let default_model = chat.current_model().to_string();
3592+
3593+
let mut plan_mask =
3594+
collaboration_modes::mask_for_kind(chat.models_manager.as_ref(), ModeKind::Plan)
3595+
.expect("expected plan collaboration mode");
3596+
plan_mask.model = Some("gpt-5.1-codex-mini".to_string());
3597+
chat.set_collaboration_mask(plan_mask);
3598+
3599+
let plan_messages = drain_insert_history(&mut rx)
3600+
.iter()
3601+
.map(|lines| lines_to_single_string(lines))
3602+
.collect::<Vec<_>>()
3603+
.join("\n");
3604+
assert!(
3605+
plan_messages.contains("Model changed to gpt-5.1-codex-mini medium for Plan mode."),
3606+
"expected Plan-mode model switch notice, got: {plan_messages:?}"
3607+
);
3608+
3609+
let default_mask = collaboration_modes::default_mask(chat.models_manager.as_ref())
3610+
.expect("expected default collaboration mode");
3611+
chat.set_collaboration_mask(default_mask);
3612+
3613+
let default_messages = drain_insert_history(&mut rx)
3614+
.iter()
3615+
.map(|lines| lines_to_single_string(lines))
3616+
.collect::<Vec<_>>()
3617+
.join("\n");
3618+
let expected_default_message =
3619+
format!("Model changed to {default_model} default for Default mode.");
3620+
assert!(
3621+
default_messages.contains(&expected_default_message),
3622+
"expected Default-mode model switch notice, got: {default_messages:?}"
3623+
);
3624+
}
3625+
3626+
#[tokio::test]
3627+
async fn mode_switch_surfaces_reasoning_change_notification_when_model_stays_same() {
3628+
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.3-codex")).await;
3629+
chat.set_feature_enabled(Feature::CollaborationModes, true);
3630+
chat.set_reasoning_effort(Some(ReasoningEffortConfig::High));
3631+
3632+
let plan_mask = collaboration_modes::plan_mask(chat.models_manager.as_ref())
3633+
.expect("expected plan collaboration mode");
3634+
chat.set_collaboration_mask(plan_mask);
3635+
3636+
let plan_messages = drain_insert_history(&mut rx)
3637+
.iter()
3638+
.map(|lines| lines_to_single_string(lines))
3639+
.collect::<Vec<_>>()
3640+
.join("\n");
3641+
assert!(
3642+
plan_messages.contains("Model changed to gpt-5.3-codex medium for Plan mode."),
3643+
"expected reasoning-change notice in Plan mode, got: {plan_messages:?}"
3644+
);
3645+
}
3646+
35873647
#[tokio::test]
35883648
async fn collab_slash_command_opens_picker_and_updates_mode() {
35893649
let (mut chat, mut rx, mut op_rx) = make_chatwidget_manual(None).await;
@@ -3649,7 +3709,12 @@ async fn plan_slash_command_switches_to_plan_mode() {
36493709

36503710
chat.dispatch_command(SlashCommand::Plan);
36513711

3652-
assert!(rx.try_recv().is_err(), "plan should not emit an app event");
3712+
while let Ok(event) = rx.try_recv() {
3713+
assert!(
3714+
matches!(event, AppEvent::InsertHistoryCell(_)),
3715+
"plan should not emit a non-history app event: {event:?}"
3716+
);
3717+
}
36533718
assert_eq!(chat.active_collaboration_mode_kind(), ModeKind::Plan);
36543719
assert_eq!(chat.current_collaboration_mode(), &initial);
36553720
}

0 commit comments

Comments
 (0)