From e63ab0dd65c4ec1ed88046c59959a86d97a891aa Mon Sep 17 00:00:00 2001 From: Ahmed Ibrahim Date: Wed, 12 Nov 2025 17:24:21 -0800 Subject: [PATCH] NUX for gpt5.1 (#6561) - Introducing a screen to inform users of model changes. - Config name is being passed to be able to reuse this component in the future for future models --- codex-rs/common/src/model_presets.rs | 9 + codex-rs/core/src/config/edit.rs | 47 +++ codex-rs/core/src/config/types.rs | 2 + codex-rs/tui/src/app.rs | 113 ++++++- codex-rs/tui/src/app_event.rs | 5 + codex-rs/tui/src/chatwidget/tests.rs | 1 + codex-rs/tui/src/lib.rs | 1 + codex-rs/tui/src/model_migration.rs | 275 ++++++++++++++++++ ...ration__tests__model_migration_prompt.snap | 15 + ...ts__model_migration_prompt_gpt5_codex.snap | 15 + ...odel_migration_prompt_gpt5_codex_mini.snap | 15 + ...s__model_migration_prompt_gpt5_family.snap | 15 + 12 files changed, 512 insertions(+), 1 deletion(-) create mode 100644 codex-rs/tui/src/model_migration.rs create mode 100644 codex-rs/tui/src/snapshots/codex_tui__model_migration__tests__model_migration_prompt.snap create mode 100644 codex-rs/tui/src/snapshots/codex_tui__model_migration__tests__model_migration_prompt_gpt5_codex.snap create mode 100644 codex-rs/tui/src/snapshots/codex_tui__model_migration__tests__model_migration_prompt_gpt5_codex_mini.snap create mode 100644 codex-rs/tui/src/snapshots/codex_tui__model_migration__tests__model_migration_prompt_gpt5_family.snap diff --git a/codex-rs/common/src/model_presets.rs b/codex-rs/common/src/model_presets.rs index 5f0cdf8fb..43d66e703 100644 --- a/codex-rs/common/src/model_presets.rs +++ b/codex-rs/common/src/model_presets.rs @@ -27,6 +27,8 @@ pub struct ModelPreset { pub supported_reasoning_efforts: &'static [ReasoningEffortPreset], /// Whether this is the default model for new users. pub is_default: bool, + /// recommended upgrade model + pub recommended_upgrade_model: Option<&'static str>, } const PRESETS: &[ModelPreset] = &[ @@ -51,6 +53,7 @@ const PRESETS: &[ModelPreset] = &[ }, ], is_default: true, + recommended_upgrade_model: Some("gpt-5.1-codex"), }, ModelPreset { id: "gpt-5-codex-mini", @@ -69,6 +72,7 @@ const PRESETS: &[ModelPreset] = &[ }, ], is_default: false, + recommended_upgrade_model: Some("gpt-5.1-codex-mini"), }, ModelPreset { id: "gpt-5", @@ -95,6 +99,7 @@ const PRESETS: &[ModelPreset] = &[ }, ], is_default: false, + recommended_upgrade_model: Some("gpt-5.1"), }, ]; @@ -107,6 +112,10 @@ pub fn builtin_model_presets(auth_mode: Option) -> Vec { .collect() } +pub fn all_model_presets() -> &'static [ModelPreset] { + PRESETS +} + #[cfg(test)] mod tests { use super::*; diff --git a/codex-rs/core/src/config/edit.rs b/codex-rs/core/src/config/edit.rs index df0b12733..b4bfdf7aa 100644 --- a/codex-rs/core/src/config/edit.rs +++ b/codex-rs/core/src/config/edit.rs @@ -29,6 +29,8 @@ pub enum ConfigEdit { SetNoticeHideRateLimitModelNudge(bool), /// Toggle the Windows onboarding acknowledgement flag. SetWindowsWslSetupAcknowledged(bool), + /// Toggle the model migration prompt acknowledgement flag. + SetNoticeHideModelMigrationPrompt(String, bool), /// Replace the entire `[mcp_servers]` table. ReplaceMcpServers(BTreeMap), /// Set trust_level = "trusted" under `[projects.""]`, @@ -253,6 +255,13 @@ impl ConfigDocument { &[Notice::TABLE_KEY, "hide_rate_limit_model_nudge"], value(*acknowledged), )), + ConfigEdit::SetNoticeHideModelMigrationPrompt(migration_config, acknowledged) => { + Ok(self.write_value( + Scope::Global, + &[Notice::TABLE_KEY, migration_config.as_str()], + value(*acknowledged), + )) + } ConfigEdit::SetWindowsWslSetupAcknowledged(acknowledged) => Ok(self.write_value( Scope::Global, &["windows_wsl_setup_acknowledged"], @@ -499,6 +508,15 @@ impl ConfigEditsBuilder { self } + pub fn set_hide_model_migration_prompt(mut self, model: &str, acknowledged: bool) -> Self { + self.edits + .push(ConfigEdit::SetNoticeHideModelMigrationPrompt( + model.to_string(), + acknowledged, + )); + self + } + pub fn set_windows_wsl_setup_acknowledged(mut self, acknowledged: bool) -> Self { self.edits .push(ConfigEdit::SetWindowsWslSetupAcknowledged(acknowledged)); @@ -770,6 +788,35 @@ existing = "value" let expected = r#"[notice] existing = "value" hide_rate_limit_model_nudge = true +"#; + assert_eq!(contents, expected); + } + #[test] + fn blocking_set_hide_gpt5_1_migration_prompt_preserves_table() { + let tmp = tempdir().expect("tmpdir"); + let codex_home = tmp.path(); + std::fs::write( + codex_home.join(CONFIG_TOML_FILE), + r#"[notice] +existing = "value" +"#, + ) + .expect("seed"); + apply_blocking( + codex_home, + None, + &[ConfigEdit::SetNoticeHideModelMigrationPrompt( + "hide_gpt5_1_migration_prompt".to_string(), + true, + )], + ) + .expect("persist"); + + let contents = + std::fs::read_to_string(codex_home.join(CONFIG_TOML_FILE)).expect("read config"); + let expected = r#"[notice] +existing = "value" +hide_gpt5_1_migration_prompt = true "#; assert_eq!(contents, expected); } diff --git a/codex-rs/core/src/config/types.rs b/codex-rs/core/src/config/types.rs index c1dae19f0..be47b20b0 100644 --- a/codex-rs/core/src/config/types.rs +++ b/codex-rs/core/src/config/types.rs @@ -362,6 +362,8 @@ pub struct Notice { pub hide_world_writable_warning: Option, /// Tracks whether the user opted out of the rate limit model switch reminder. pub hide_rate_limit_model_nudge: Option, + /// Tracks whether the user has seen the model migration prompt + pub hide_gpt5_1_migration_prompt: Option, } impl Notice { diff --git a/codex-rs/tui/src/app.rs b/codex-rs/tui/src/app.rs index ed5a2415b..720976561 100644 --- a/codex-rs/tui/src/app.rs +++ b/codex-rs/tui/src/app.rs @@ -7,6 +7,8 @@ use crate::diff_render::DiffSummary; use crate::exec_command::strip_bash_lc_and_escape; use crate::file_search::FileSearchManager; use crate::history_cell::HistoryCell; +use crate::model_migration::ModelMigrationOutcome; +use crate::model_migration::run_model_migration_prompt; use crate::pager_overlay::Overlay; use crate::render::highlight::highlight_bash_to_lines; use crate::render::renderable::Renderable; @@ -15,6 +17,7 @@ use crate::tui; use crate::tui::TuiEvent; use crate::update_action::UpdateAction; use codex_ansi_escape::ansi_escape_line; +use codex_common::model_presets::all_model_presets; use codex_core::AuthManager; use codex_core::ConversationManager; use codex_core::config::Config; @@ -50,6 +53,64 @@ pub struct AppExitInfo { pub update_action: Option, } +fn should_show_model_migration_prompt( + current_model: &str, + target_model: &str, + hide_prompt_flag: Option, +) -> bool { + if target_model == current_model || hide_prompt_flag.unwrap_or(false) { + return false; + } + + all_model_presets() + .iter() + .filter(|preset| preset.recommended_upgrade_model.is_some()) + .any(|preset| preset.model == current_model) +} + +async fn handle_model_migration_prompt_if_needed( + tui: &mut tui::Tui, + config: &mut Config, + app_event_tx: &AppEventSender, +) -> Option { + let target_model = all_model_presets() + .iter() + .find(|preset| preset.model == config.model) + .and_then(|preset| preset.recommended_upgrade_model) + .unwrap_or(&config.model) + .to_string(); + let hide_prompt_flag = config.notices.hide_gpt5_1_migration_prompt; + if !should_show_model_migration_prompt(&config.model, &target_model, hide_prompt_flag) { + return None; + } + + match run_model_migration_prompt(tui, &target_model).await { + ModelMigrationOutcome::Accepted => { + app_event_tx.send(AppEvent::PersistModelMigrationPromptAcknowledged { + migration_config: "hide_gpt5_1_migration_prompt".to_string(), + }); + config.model = target_model.clone(); + if let Some(family) = find_family_for_model(&target_model) { + config.model_family = family; + } + app_event_tx.send(AppEvent::UpdateModel(target_model.clone())); + app_event_tx.send(AppEvent::PersistModelSelection { + model: target_model, + effort: config.model_reasoning_effort, + }); + } + ModelMigrationOutcome::Exit => { + return Some(AppExitInfo { + token_usage: TokenUsage::default(), + conversation_id: None, + update_action: None, + }); + } + } + + None +} + pub(crate) struct App { pub(crate) server: Arc, pub(crate) app_event_tx: AppEventSender, @@ -89,7 +150,7 @@ impl App { pub async fn run( tui: &mut tui::Tui, auth_manager: Arc, - config: Config, + mut config: Config, active_profile: Option, initial_prompt: Option, initial_images: Vec, @@ -100,6 +161,12 @@ impl App { let (app_event_tx, mut app_event_rx) = unbounded_channel(); let app_event_tx = AppEventSender::new(app_event_tx); + let exit_info = + handle_model_migration_prompt_if_needed(tui, &mut config, &app_event_tx).await; + if let Some(exit_info) = exit_info { + return Ok(exit_info); + } + let conversation_manager = Arc::new(ConversationManager::new( auth_manager.clone(), SessionSource::Cli, @@ -547,6 +614,18 @@ impl App { )); } } + AppEvent::PersistModelMigrationPromptAcknowledged { migration_config } => { + if let Err(err) = ConfigEditsBuilder::new(&self.config.codex_home) + .set_hide_model_migration_prompt(&migration_config, true) + .apply() + .await + { + tracing::error!(error = %err, "failed to persist model migration prompt acknowledgement"); + self.chat_widget.add_error_message(format!( + "Failed to save model migration prompt preference: {err}" + )); + } + } AppEvent::OpenApprovalsPopup => { self.chat_widget.open_approvals_popup(); } @@ -757,6 +836,38 @@ mod tests { } } + #[test] + fn model_migration_prompt_only_shows_for_deprecated_models() { + assert!(should_show_model_migration_prompt("gpt-5", "gpt-5.1", None)); + assert!(should_show_model_migration_prompt( + "gpt-5-codex", + "gpt-5.1-codex", + None + )); + assert!(should_show_model_migration_prompt( + "gpt-5-codex-mini", + "gpt-5.1-codex-mini", + None + )); + assert!(!should_show_model_migration_prompt( + "gpt-5.1-codex", + "gpt-5.1-codex", + None + )); + } + + #[test] + fn model_migration_prompt_respects_hide_flag_and_self_target() { + assert!(!should_show_model_migration_prompt( + "gpt-5", + "gpt-5.1", + Some(true) + )); + assert!(!should_show_model_migration_prompt( + "gpt-5.1", "gpt-5.1", None + )); + } + #[test] fn update_reasoning_effort_updates_config() { let mut app = make_test_app(); diff --git a/codex-rs/tui/src/app_event.rs b/codex-rs/tui/src/app_event.rs index 54850d69b..39485faa9 100644 --- a/codex-rs/tui/src/app_event.rs +++ b/codex-rs/tui/src/app_event.rs @@ -117,6 +117,11 @@ pub(crate) enum AppEvent { /// Persist the acknowledgement flag for the rate limit switch prompt. PersistRateLimitSwitchPromptHidden, + /// Persist the acknowledgement flag for the model migration prompt. + PersistModelMigrationPromptAcknowledged { + migration_config: String, + }, + /// Skip the next world-writable scan (one-shot) after a user-confirmed continue. #[cfg_attr(not(target_os = "windows"), allow(dead_code))] SkipNextWorldWritableScan, diff --git a/codex-rs/tui/src/chatwidget/tests.rs b/codex-rs/tui/src/chatwidget/tests.rs index f3c13c0fb..cc917a478 100644 --- a/codex-rs/tui/src/chatwidget/tests.rs +++ b/codex-rs/tui/src/chatwidget/tests.rs @@ -1533,6 +1533,7 @@ fn single_reasoning_option_skips_selection() { default_reasoning_effort: ReasoningEffortConfig::High, supported_reasoning_efforts: &SINGLE_EFFORT, is_default: false, + recommended_upgrade_model: None, }; chat.open_reasoning_popup(preset); diff --git a/codex-rs/tui/src/lib.rs b/codex-rs/tui/src/lib.rs index 12fc1f813..d7b674a9d 100644 --- a/codex-rs/tui/src/lib.rs +++ b/codex-rs/tui/src/lib.rs @@ -54,6 +54,7 @@ pub mod live_wrap; mod markdown; mod markdown_render; mod markdown_stream; +mod model_migration; pub mod onboarding; mod pager_overlay; pub mod public_widgets; diff --git a/codex-rs/tui/src/model_migration.rs b/codex-rs/tui/src/model_migration.rs new file mode 100644 index 000000000..8a1482e12 --- /dev/null +++ b/codex-rs/tui/src/model_migration.rs @@ -0,0 +1,275 @@ +use crate::render::Insets; +use crate::render::renderable::ColumnRenderable; +use crate::render::renderable::Renderable; +use crate::render::renderable::RenderableExt as _; +use crate::tui::FrameRequester; +use crate::tui::Tui; +use crate::tui::TuiEvent; +use crossterm::event::KeyCode; +use crossterm::event::KeyEvent; +use crossterm::event::KeyEventKind; +use crossterm::event::KeyModifiers; +use ratatui::prelude::Stylize as _; +use ratatui::prelude::Widget; +use ratatui::text::Line; +use ratatui::widgets::Clear; +use ratatui::widgets::Paragraph; +use ratatui::widgets::WidgetRef; +use ratatui::widgets::Wrap; +use tokio_stream::StreamExt; + +/// Outcome of the migration prompt. +pub(crate) enum ModelMigrationOutcome { + Accepted, + Exit, +} + +pub(crate) async fn run_model_migration_prompt( + tui: &mut Tui, + target_model: &str, +) -> ModelMigrationOutcome { + // Render the prompt on the terminal's alternate screen so exiting or cancelling + // does not leave a large blank region in the normal scrollback. This does not + // change the prompt's appearance – only where it is drawn. + struct AltScreenGuard<'a> { + tui: &'a mut Tui, + } + impl<'a> AltScreenGuard<'a> { + fn enter(tui: &'a mut Tui) -> Self { + let _ = tui.enter_alt_screen(); + Self { tui } + } + } + impl Drop for AltScreenGuard<'_> { + fn drop(&mut self) { + let _ = self.tui.leave_alt_screen(); + } + } + + let alt = AltScreenGuard::enter(tui); + + let mut screen = ModelMigrationScreen::new(alt.tui.frame_requester(), target_model); + + let _ = alt.tui.draw(u16::MAX, |frame| { + frame.render_widget_ref(&screen, frame.area()); + }); + + let events = alt.tui.event_stream(); + tokio::pin!(events); + + while !screen.is_done() { + if let Some(event) = events.next().await { + match event { + TuiEvent::Key(key_event) => screen.handle_key(key_event), + TuiEvent::Paste(_) => {} + TuiEvent::Draw => { + let _ = alt.tui.draw(u16::MAX, |frame| { + frame.render_widget_ref(&screen, frame.area()); + }); + } + } + } else { + screen.accept(); + break; + } + } + + screen.outcome() +} + +struct ModelMigrationScreen { + request_frame: FrameRequester, + target_model: String, + done: bool, + should_exit: bool, +} + +impl ModelMigrationScreen { + fn new(request_frame: FrameRequester, target_model: &str) -> Self { + Self { + request_frame, + target_model: target_model.to_string(), + done: false, + should_exit: false, + } + } + + fn accept(&mut self) { + self.done = true; + self.request_frame.schedule_frame(); + } + + fn handle_key(&mut self, key_event: KeyEvent) { + if key_event.kind == KeyEventKind::Release { + return; + } + + if key_event.modifiers.contains(KeyModifiers::CONTROL) + && matches!(key_event.code, KeyCode::Char('c') | KeyCode::Char('d')) + { + self.should_exit = true; + self.done = true; + self.request_frame.schedule_frame(); + return; + } + + if matches!(key_event.code, KeyCode::Esc | KeyCode::Enter) { + self.accept(); + } + } + + fn is_done(&self) -> bool { + self.done + } + + fn outcome(&self) -> ModelMigrationOutcome { + if self.should_exit { + ModelMigrationOutcome::Exit + } else { + ModelMigrationOutcome::Accepted + } + } +} + +impl WidgetRef for &ModelMigrationScreen { + fn render_ref(&self, area: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) { + Clear.render(area, buf); + + let mut column = ColumnRenderable::new(); + + column.push(""); + column.push(Line::from(vec![ + "> ".into(), + "Introducing ".bold(), + "our ".bold(), + self.target_model.clone().bold(), + " models".bold(), + ])); + column.push(Line::from("")); + + column.push( + Paragraph::new(Line::from( + "We've upgraded our family of models supported in Codex to gpt-5.1, gpt-5.1-codex and gpt-5.1-codex-mini.", + )) + .wrap(Wrap { trim: false }) + .inset(Insets::tlbr(0, 2, 0, 0)), + ); + column.push(Line::from("")); + column.push( + Paragraph::new(Line::from( + "You can continue using legacy models by specifying them directly with the -m option or in your config.toml.", + )) + .wrap(Wrap { trim: false }) + .inset(Insets::tlbr(0, 2, 0, 0)), + ); + column.push(Line::from("")); + column.push( + Line::from(vec![ + "Learn more at ".into(), + "www.openai.com/index/gpt-5-1".cyan().underlined(), + ".".into(), + ]) + .inset(Insets::tlbr(0, 2, 0, 0)), + ); + column.push(Line::from("")); + column.push( + Line::from(vec!["Press enter to continue".dim()]).inset(Insets::tlbr(0, 2, 0, 0)), + ); + + column.render(area, buf); + } +} + +#[cfg(test)] +mod tests { + use super::ModelMigrationScreen; + use crate::custom_terminal::Terminal; + use crate::test_backend::VT100Backend; + use crate::tui::FrameRequester; + use crossterm::event::KeyCode; + use crossterm::event::KeyEvent; + use insta::assert_snapshot; + use ratatui::layout::Rect; + + #[test] + fn prompt_snapshot() { + let width: u16 = 60; + let height: u16 = 12; + let backend = VT100Backend::new(width, height); + let mut terminal = Terminal::with_options(backend).expect("terminal"); + terminal.set_viewport_area(Rect::new(0, 0, width, height)); + + let screen = ModelMigrationScreen::new(FrameRequester::test_dummy(), "gpt-5.1-codex"); + + { + let mut frame = terminal.get_frame(); + frame.render_widget_ref(&screen, frame.area()); + } + terminal.flush().expect("flush"); + + assert_snapshot!("model_migration_prompt", terminal.backend()); + } + + #[test] + fn prompt_snapshot_gpt5_family() { + let backend = VT100Backend::new(65, 12); + let mut terminal = Terminal::with_options(backend).expect("terminal"); + terminal.set_viewport_area(Rect::new(0, 0, 65, 12)); + + let screen = ModelMigrationScreen::new(FrameRequester::test_dummy(), "gpt-5.1"); + { + let mut frame = terminal.get_frame(); + frame.render_widget_ref(&screen, frame.area()); + } + terminal.flush().expect("flush"); + assert_snapshot!("model_migration_prompt_gpt5_family", terminal.backend()); + } + + #[test] + fn prompt_snapshot_gpt5_codex() { + let backend = VT100Backend::new(60, 12); + let mut terminal = Terminal::with_options(backend).expect("terminal"); + terminal.set_viewport_area(Rect::new(0, 0, 60, 12)); + + let screen = ModelMigrationScreen::new(FrameRequester::test_dummy(), "gpt-5.1-codex"); + { + let mut frame = terminal.get_frame(); + frame.render_widget_ref(&screen, frame.area()); + } + terminal.flush().expect("flush"); + assert_snapshot!("model_migration_prompt_gpt5_codex", terminal.backend()); + } + + #[test] + fn prompt_snapshot_gpt5_codex_mini() { + let backend = VT100Backend::new(60, 12); + let mut terminal = Terminal::with_options(backend).expect("terminal"); + terminal.set_viewport_area(Rect::new(0, 0, 60, 12)); + + let screen = ModelMigrationScreen::new(FrameRequester::test_dummy(), "gpt-5.1-codex-mini"); + { + let mut frame = terminal.get_frame(); + frame.render_widget_ref(&screen, frame.area()); + } + terminal.flush().expect("flush"); + assert_snapshot!("model_migration_prompt_gpt5_codex_mini", terminal.backend()); + } + + #[test] + fn escape_key_accepts_prompt() { + let screen_target = "gpt-5.1-codex"; + let mut screen = ModelMigrationScreen::new(FrameRequester::test_dummy(), screen_target); + + // Simulate pressing Escape + screen.handle_key(KeyEvent::new( + KeyCode::Esc, + crossterm::event::KeyModifiers::NONE, + )); + assert!(screen.is_done()); + // Esc should not be treated as Exit – it accepts like Enter. + assert!(matches!( + screen.outcome(), + super::ModelMigrationOutcome::Accepted + )); + } +} diff --git a/codex-rs/tui/src/snapshots/codex_tui__model_migration__tests__model_migration_prompt.snap b/codex-rs/tui/src/snapshots/codex_tui__model_migration__tests__model_migration_prompt.snap new file mode 100644 index 000000000..aeef4875d --- /dev/null +++ b/codex-rs/tui/src/snapshots/codex_tui__model_migration__tests__model_migration_prompt.snap @@ -0,0 +1,15 @@ +--- +source: tui/src/model_migration.rs +expression: terminal.backend() +--- +> Introducing our gpt-5.1-codex models + + We've upgraded our family of models supported in Codex to + gpt-5.1, gpt-5.1-codex and gpt-5.1-codex-mini. + + You can continue using legacy models by specifying them + directly with the -m option or in your config.toml. + + Learn more at www.openai.com/index/gpt-5-1. + + Press enter to continue diff --git a/codex-rs/tui/src/snapshots/codex_tui__model_migration__tests__model_migration_prompt_gpt5_codex.snap b/codex-rs/tui/src/snapshots/codex_tui__model_migration__tests__model_migration_prompt_gpt5_codex.snap new file mode 100644 index 000000000..aeef4875d --- /dev/null +++ b/codex-rs/tui/src/snapshots/codex_tui__model_migration__tests__model_migration_prompt_gpt5_codex.snap @@ -0,0 +1,15 @@ +--- +source: tui/src/model_migration.rs +expression: terminal.backend() +--- +> Introducing our gpt-5.1-codex models + + We've upgraded our family of models supported in Codex to + gpt-5.1, gpt-5.1-codex and gpt-5.1-codex-mini. + + You can continue using legacy models by specifying them + directly with the -m option or in your config.toml. + + Learn more at www.openai.com/index/gpt-5-1. + + Press enter to continue diff --git a/codex-rs/tui/src/snapshots/codex_tui__model_migration__tests__model_migration_prompt_gpt5_codex_mini.snap b/codex-rs/tui/src/snapshots/codex_tui__model_migration__tests__model_migration_prompt_gpt5_codex_mini.snap new file mode 100644 index 000000000..9ca1eb450 --- /dev/null +++ b/codex-rs/tui/src/snapshots/codex_tui__model_migration__tests__model_migration_prompt_gpt5_codex_mini.snap @@ -0,0 +1,15 @@ +--- +source: tui/src/model_migration.rs +expression: terminal.backend() +--- +> Introducing our gpt-5.1-codex-mini models + + We've upgraded our family of models supported in Codex to + gpt-5.1, gpt-5.1-codex and gpt-5.1-codex-mini. + + You can continue using legacy models by specifying them + directly with the -m option or in your config.toml. + + Learn more at www.openai.com/index/gpt-5-1. + + Press enter to continue diff --git a/codex-rs/tui/src/snapshots/codex_tui__model_migration__tests__model_migration_prompt_gpt5_family.snap b/codex-rs/tui/src/snapshots/codex_tui__model_migration__tests__model_migration_prompt_gpt5_family.snap new file mode 100644 index 000000000..5a0ccd9b5 --- /dev/null +++ b/codex-rs/tui/src/snapshots/codex_tui__model_migration__tests__model_migration_prompt_gpt5_family.snap @@ -0,0 +1,15 @@ +--- +source: tui/src/model_migration.rs +expression: terminal.backend() +--- +> Introducing our gpt-5.1 models + + We've upgraded our family of models supported in Codex to + gpt-5.1, gpt-5.1-codex and gpt-5.1-codex-mini. + + You can continue using legacy models by specifying them + directly with the -m option or in your config.toml. + + Learn more at www.openai.com/index/gpt-5-1. + + Press enter to continue