mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Fix stale model test fixtures (#18719)
Fixes stale test fixtures left after the active bundled model catalog updates in #18586 and #18388. Those changes made `gpt-5.4` the current default and removed several older hardcoded slugs, which left Windows Bazel shards failing TUI and config tests. What changed: - Refresh TUI model migration, availability NUX, plan-mode, status, and snapshot fixtures to use active bundled model slugs. - Update the config edit test expectation for the TOML-quoted `"gpt-5.2"` migration key. - Move the model catalog tests into `codex-rs/tui/src/app/tests/model_catalog.rs` so touching them does not trip the blob-size policy for `app.rs`. Verification: - CI Bazel/lint checks are expected to cover the affected test shards.
This commit is contained in:
committed by
GitHub
Unverified
parent
baa5dd7b29
commit
cc96a03f10
@@ -547,7 +547,7 @@ existing = "value"
|
||||
existing = "value"
|
||||
|
||||
[notice.model_migrations]
|
||||
gpt-5 = "gpt-5.4"
|
||||
"gpt-5.2" = "gpt-5.4"
|
||||
"#;
|
||||
assert_eq!(contents, expected);
|
||||
}
|
||||
|
||||
+2
-308
@@ -7114,7 +7114,6 @@ mod tests {
|
||||
use codex_app_server_protocol::TurnStatus;
|
||||
use codex_app_server_protocol::UserInput as AppServerUserInput;
|
||||
use codex_app_server_protocol::WarningNotification;
|
||||
use codex_config::types::ModelAvailabilityNuxConfig;
|
||||
use codex_otel::SessionTelemetry;
|
||||
use codex_protocol::ThreadId;
|
||||
use codex_protocol::config_types::CollaborationMode;
|
||||
@@ -7125,7 +7124,6 @@ mod tests {
|
||||
use codex_protocol::models::FileSystemPermissions;
|
||||
use codex_protocol::models::NetworkPermissions;
|
||||
use codex_protocol::models::PermissionProfile;
|
||||
use codex_protocol::openai_models::ModelAvailabilityNux;
|
||||
use codex_protocol::protocol::AskForApproval;
|
||||
use codex_protocol::protocol::Event;
|
||||
use codex_protocol::protocol::EventMsg;
|
||||
@@ -7152,6 +7150,8 @@ mod tests {
|
||||
use tempfile::tempdir;
|
||||
use tokio::time;
|
||||
|
||||
mod model_catalog;
|
||||
|
||||
fn test_absolute_path(path: &str) -> AbsolutePathBuf {
|
||||
AbsolutePathBuf::try_from(PathBuf::from(path)).expect("absolute test path")
|
||||
}
|
||||
@@ -11474,135 +11474,6 @@ guardian_approval = true
|
||||
.and_then(TomlValue::as_bool)
|
||||
}
|
||||
|
||||
fn all_model_presets() -> Vec<ModelPreset> {
|
||||
crate::legacy_core::test_support::all_model_presets().clone()
|
||||
}
|
||||
|
||||
fn model_availability_nux_config(shown_count: &[(&str, u32)]) -> ModelAvailabilityNuxConfig {
|
||||
ModelAvailabilityNuxConfig {
|
||||
shown_count: shown_count
|
||||
.iter()
|
||||
.map(|(model, count)| ((*model).to_string(), *count))
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
fn model_migration_copy_to_plain_text(
|
||||
copy: &crate::model_migration::ModelMigrationCopy,
|
||||
) -> String {
|
||||
if let Some(markdown) = copy.markdown.as_ref() {
|
||||
return markdown.clone();
|
||||
}
|
||||
let mut s = String::new();
|
||||
for span in ©.heading {
|
||||
s.push_str(&span.content);
|
||||
}
|
||||
s.push('\n');
|
||||
s.push('\n');
|
||||
for line in ©.content {
|
||||
for span in &line.spans {
|
||||
s.push_str(&span.content);
|
||||
}
|
||||
s.push('\n');
|
||||
}
|
||||
s
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn model_migration_prompt_only_shows_for_deprecated_models() {
|
||||
let seen = BTreeMap::new();
|
||||
assert!(should_show_model_migration_prompt(
|
||||
"gpt-5",
|
||||
"gpt-5.2-codex",
|
||||
&seen,
|
||||
&all_model_presets()
|
||||
));
|
||||
assert!(should_show_model_migration_prompt(
|
||||
"gpt-5-codex",
|
||||
"gpt-5.2-codex",
|
||||
&seen,
|
||||
&all_model_presets()
|
||||
));
|
||||
assert!(should_show_model_migration_prompt(
|
||||
"gpt-5-codex-mini",
|
||||
"gpt-5.2-codex",
|
||||
&seen,
|
||||
&all_model_presets()
|
||||
));
|
||||
assert!(should_show_model_migration_prompt(
|
||||
"gpt-5.1-codex",
|
||||
"gpt-5.2-codex",
|
||||
&seen,
|
||||
&all_model_presets()
|
||||
));
|
||||
assert!(!should_show_model_migration_prompt(
|
||||
"gpt-5.1-codex",
|
||||
"gpt-5.1-codex",
|
||||
&seen,
|
||||
&all_model_presets()
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn select_model_availability_nux_picks_only_eligible_model() {
|
||||
let mut presets = all_model_presets();
|
||||
presets.iter_mut().for_each(|preset| {
|
||||
preset.availability_nux = None;
|
||||
});
|
||||
let target = presets
|
||||
.iter_mut()
|
||||
.find(|preset| preset.model == "gpt-5")
|
||||
.expect("target preset present");
|
||||
target.availability_nux = Some(ModelAvailabilityNux {
|
||||
message: "gpt-5 is available".to_string(),
|
||||
});
|
||||
|
||||
let selected = select_model_availability_nux(&presets, &model_availability_nux_config(&[]));
|
||||
|
||||
assert_eq!(
|
||||
selected,
|
||||
Some(StartupTooltipOverride {
|
||||
model_slug: "gpt-5".to_string(),
|
||||
message: "gpt-5 is available".to_string(),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn select_model_availability_nux_skips_missing_and_exhausted_models() {
|
||||
let mut presets = all_model_presets();
|
||||
presets.iter_mut().for_each(|preset| {
|
||||
preset.availability_nux = None;
|
||||
});
|
||||
let gpt_5 = presets
|
||||
.iter_mut()
|
||||
.find(|preset| preset.model == "gpt-5")
|
||||
.expect("gpt-5 preset present");
|
||||
gpt_5.availability_nux = Some(ModelAvailabilityNux {
|
||||
message: "gpt-5 is available".to_string(),
|
||||
});
|
||||
let gpt_5_2 = presets
|
||||
.iter_mut()
|
||||
.find(|preset| preset.model == "gpt-5.2")
|
||||
.expect("gpt-5.2 preset present");
|
||||
gpt_5_2.availability_nux = Some(ModelAvailabilityNux {
|
||||
message: "gpt-5.2 is available".to_string(),
|
||||
});
|
||||
|
||||
let selected = select_model_availability_nux(
|
||||
&presets,
|
||||
&model_availability_nux_config(&[("gpt-5", MODEL_AVAILABILITY_NUX_MAX_SHOW_COUNT)]),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
selected,
|
||||
Some(StartupTooltipOverride {
|
||||
model_slug: "gpt-5.2".to_string(),
|
||||
message: "gpt-5.2 is available".to_string(),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn active_turn_not_steerable_turn_error_extracts_structured_server_error() {
|
||||
let turn_error = AppServerTurnError {
|
||||
@@ -11665,183 +11536,6 @@ guardian_approval = true
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn select_model_availability_nux_uses_existing_model_order_as_priority() {
|
||||
let mut presets = all_model_presets();
|
||||
presets.iter_mut().for_each(|preset| {
|
||||
preset.availability_nux = None;
|
||||
});
|
||||
let first = presets
|
||||
.iter_mut()
|
||||
.find(|preset| preset.model == "gpt-5")
|
||||
.expect("gpt-5 preset present");
|
||||
first.availability_nux = Some(ModelAvailabilityNux {
|
||||
message: "first".to_string(),
|
||||
});
|
||||
let second = presets
|
||||
.iter_mut()
|
||||
.find(|preset| preset.model == "gpt-5.2")
|
||||
.expect("gpt-5.2 preset present");
|
||||
second.availability_nux = Some(ModelAvailabilityNux {
|
||||
message: "second".to_string(),
|
||||
});
|
||||
|
||||
let selected = select_model_availability_nux(&presets, &model_availability_nux_config(&[]));
|
||||
|
||||
assert_eq!(
|
||||
selected,
|
||||
Some(StartupTooltipOverride {
|
||||
model_slug: "gpt-5.2".to_string(),
|
||||
message: "second".to_string(),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn select_model_availability_nux_returns_none_when_all_models_are_exhausted() {
|
||||
let mut presets = all_model_presets();
|
||||
presets.iter_mut().for_each(|preset| {
|
||||
preset.availability_nux = None;
|
||||
});
|
||||
let target = presets
|
||||
.iter_mut()
|
||||
.find(|preset| preset.model == "gpt-5")
|
||||
.expect("target preset present");
|
||||
target.availability_nux = Some(ModelAvailabilityNux {
|
||||
message: "gpt-5 is available".to_string(),
|
||||
});
|
||||
|
||||
let selected = select_model_availability_nux(
|
||||
&presets,
|
||||
&model_availability_nux_config(&[("gpt-5", MODEL_AVAILABILITY_NUX_MAX_SHOW_COUNT)]),
|
||||
);
|
||||
|
||||
assert_eq!(selected, None);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn model_migration_prompt_respects_hide_flag_and_self_target() {
|
||||
let mut seen = BTreeMap::new();
|
||||
seen.insert("gpt-5".to_string(), "gpt-5.1".to_string());
|
||||
assert!(!should_show_model_migration_prompt(
|
||||
"gpt-5",
|
||||
"gpt-5.1",
|
||||
&seen,
|
||||
&all_model_presets()
|
||||
));
|
||||
assert!(!should_show_model_migration_prompt(
|
||||
"gpt-5.1",
|
||||
"gpt-5.1",
|
||||
&seen,
|
||||
&all_model_presets()
|
||||
));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn model_migration_prompt_skips_when_target_missing_or_hidden() {
|
||||
let mut available = all_model_presets();
|
||||
let mut current = available
|
||||
.iter()
|
||||
.find(|preset| preset.model == "gpt-5-codex")
|
||||
.cloned()
|
||||
.expect("preset present");
|
||||
current.upgrade = Some(ModelUpgrade {
|
||||
id: "missing-target".to_string(),
|
||||
reasoning_effort_mapping: None,
|
||||
migration_config_key: HIDE_GPT5_1_MIGRATION_PROMPT_CONFIG.to_string(),
|
||||
model_link: None,
|
||||
upgrade_copy: None,
|
||||
migration_markdown: None,
|
||||
});
|
||||
available.retain(|preset| preset.model != "gpt-5-codex");
|
||||
available.push(current.clone());
|
||||
|
||||
assert!(!should_show_model_migration_prompt(
|
||||
¤t.model,
|
||||
"missing-target",
|
||||
&BTreeMap::new(),
|
||||
&available,
|
||||
));
|
||||
|
||||
assert!(target_preset_for_upgrade(&available, "missing-target").is_none());
|
||||
|
||||
let mut with_hidden_target = all_model_presets();
|
||||
let target = with_hidden_target
|
||||
.iter_mut()
|
||||
.find(|preset| preset.model == "gpt-5.2-codex")
|
||||
.expect("target preset present");
|
||||
target.show_in_picker = false;
|
||||
|
||||
assert!(!should_show_model_migration_prompt(
|
||||
"gpt-5-codex",
|
||||
"gpt-5.2-codex",
|
||||
&BTreeMap::new(),
|
||||
&with_hidden_target,
|
||||
));
|
||||
assert!(target_preset_for_upgrade(&with_hidden_target, "gpt-5.2-codex").is_none());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn model_migration_prompt_shows_for_hidden_model() {
|
||||
let codex_home = tempdir().expect("temp codex home");
|
||||
let config = ConfigBuilder::default()
|
||||
.codex_home(codex_home.path().to_path_buf())
|
||||
.build()
|
||||
.await
|
||||
.expect("config");
|
||||
|
||||
let mut available_models = all_model_presets();
|
||||
let current = available_models
|
||||
.iter()
|
||||
.find(|preset| preset.model == "gpt-5.1-codex")
|
||||
.cloned()
|
||||
.expect("gpt-5.1-codex preset present");
|
||||
assert!(
|
||||
!current.show_in_picker,
|
||||
"expected gpt-5.1-codex to be hidden from picker for this test"
|
||||
);
|
||||
|
||||
let upgrade = current.upgrade.as_ref().expect("upgrade configured");
|
||||
// Test "hidden current model still prompts" even if bundled
|
||||
// catalog data changes the target model's picker visibility.
|
||||
available_models
|
||||
.iter_mut()
|
||||
.find(|preset| preset.model == upgrade.id)
|
||||
.expect("upgrade target present")
|
||||
.show_in_picker = true;
|
||||
assert!(
|
||||
should_show_model_migration_prompt(
|
||||
¤t.model,
|
||||
&upgrade.id,
|
||||
&config.notices.model_migrations,
|
||||
&available_models,
|
||||
),
|
||||
"expected migration prompt to be eligible for hidden model"
|
||||
);
|
||||
|
||||
let target = target_preset_for_upgrade(&available_models, &upgrade.id)
|
||||
.expect("upgrade target present");
|
||||
let target_description =
|
||||
(!target.description.is_empty()).then(|| target.description.clone());
|
||||
let can_opt_out = true;
|
||||
let copy = migration_copy_for_models(
|
||||
¤t.model,
|
||||
&upgrade.id,
|
||||
upgrade.model_link.clone(),
|
||||
upgrade.upgrade_copy.clone(),
|
||||
upgrade.migration_markdown.clone(),
|
||||
target.display_name.clone(),
|
||||
target_description,
|
||||
can_opt_out,
|
||||
);
|
||||
|
||||
// Snapshot the copy we would show; rendering is covered by model_migration snapshots.
|
||||
assert_snapshot!(
|
||||
"model_migration_prompt_shows_for_hidden_model",
|
||||
model_migration_copy_to_plain_text(©)
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn update_reasoning_effort_updates_collaboration_mode() {
|
||||
let mut app = make_test_app().await;
|
||||
|
||||
@@ -0,0 +1,293 @@
|
||||
use super::*;
|
||||
use codex_config::types::ModelAvailabilityNuxConfig;
|
||||
use codex_protocol::openai_models::ModelAvailabilityNux;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
fn all_model_presets() -> Vec<ModelPreset> {
|
||||
crate::legacy_core::test_support::all_model_presets().clone()
|
||||
}
|
||||
|
||||
fn model_availability_nux_config(shown_count: &[(&str, u32)]) -> ModelAvailabilityNuxConfig {
|
||||
ModelAvailabilityNuxConfig {
|
||||
shown_count: shown_count
|
||||
.iter()
|
||||
.map(|(model, count)| ((*model).to_string(), *count))
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
fn model_migration_copy_to_plain_text(copy: &crate::model_migration::ModelMigrationCopy) -> String {
|
||||
if let Some(markdown) = copy.markdown.as_ref() {
|
||||
return markdown.clone();
|
||||
}
|
||||
let mut s = String::new();
|
||||
for span in ©.heading {
|
||||
s.push_str(&span.content);
|
||||
}
|
||||
s.push('\n');
|
||||
s.push('\n');
|
||||
for line in ©.content {
|
||||
for span in &line.spans {
|
||||
s.push_str(&span.content);
|
||||
}
|
||||
s.push('\n');
|
||||
}
|
||||
s
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn model_migration_prompt_only_shows_for_deprecated_models() {
|
||||
let seen = BTreeMap::new();
|
||||
assert!(should_show_model_migration_prompt(
|
||||
"gpt-5.2",
|
||||
"gpt-5.4",
|
||||
&seen,
|
||||
&all_model_presets()
|
||||
));
|
||||
assert!(should_show_model_migration_prompt(
|
||||
"gpt-5.3-codex",
|
||||
"gpt-5.4",
|
||||
&seen,
|
||||
&all_model_presets()
|
||||
));
|
||||
assert!(!should_show_model_migration_prompt(
|
||||
"gpt-5.3-codex",
|
||||
"gpt-5.3-codex",
|
||||
&seen,
|
||||
&all_model_presets()
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn select_model_availability_nux_picks_only_eligible_model() {
|
||||
let mut presets = all_model_presets();
|
||||
presets.iter_mut().for_each(|preset| {
|
||||
preset.availability_nux = None;
|
||||
});
|
||||
let target = presets
|
||||
.iter_mut()
|
||||
.find(|preset| preset.model == "gpt-5.4")
|
||||
.expect("target preset present");
|
||||
target.availability_nux = Some(ModelAvailabilityNux {
|
||||
message: "gpt-5.4 is available".to_string(),
|
||||
});
|
||||
|
||||
let selected = select_model_availability_nux(&presets, &model_availability_nux_config(&[]));
|
||||
|
||||
assert_eq!(
|
||||
selected,
|
||||
Some(StartupTooltipOverride {
|
||||
model_slug: "gpt-5.4".to_string(),
|
||||
message: "gpt-5.4 is available".to_string(),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn select_model_availability_nux_skips_missing_and_exhausted_models() {
|
||||
let mut presets = all_model_presets();
|
||||
presets.iter_mut().for_each(|preset| {
|
||||
preset.availability_nux = None;
|
||||
});
|
||||
let gpt_5 = presets
|
||||
.iter_mut()
|
||||
.find(|preset| preset.model == "gpt-5.4")
|
||||
.expect("gpt-5.4 preset present");
|
||||
gpt_5.availability_nux = Some(ModelAvailabilityNux {
|
||||
message: "gpt-5.4 is available".to_string(),
|
||||
});
|
||||
let gpt_5_2 = presets
|
||||
.iter_mut()
|
||||
.find(|preset| preset.model == "gpt-5.4-mini")
|
||||
.expect("gpt-5.4-mini preset present");
|
||||
gpt_5_2.availability_nux = Some(ModelAvailabilityNux {
|
||||
message: "gpt-5.4-mini is available".to_string(),
|
||||
});
|
||||
|
||||
let selected = select_model_availability_nux(
|
||||
&presets,
|
||||
&model_availability_nux_config(&[("gpt-5.4", MODEL_AVAILABILITY_NUX_MAX_SHOW_COUNT)]),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
selected,
|
||||
Some(StartupTooltipOverride {
|
||||
model_slug: "gpt-5.4-mini".to_string(),
|
||||
message: "gpt-5.4-mini is available".to_string(),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn select_model_availability_nux_uses_existing_model_order_as_priority() {
|
||||
let mut presets = all_model_presets();
|
||||
presets.iter_mut().for_each(|preset| {
|
||||
preset.availability_nux = None;
|
||||
});
|
||||
let first = presets
|
||||
.iter_mut()
|
||||
.find(|preset| preset.model == "gpt-5.4-mini")
|
||||
.expect("gpt-5.4-mini preset present");
|
||||
first.availability_nux = Some(ModelAvailabilityNux {
|
||||
message: "first".to_string(),
|
||||
});
|
||||
let second = presets
|
||||
.iter_mut()
|
||||
.find(|preset| preset.model == "gpt-5.4")
|
||||
.expect("gpt-5.4 preset present");
|
||||
second.availability_nux = Some(ModelAvailabilityNux {
|
||||
message: "second".to_string(),
|
||||
});
|
||||
|
||||
let selected = select_model_availability_nux(&presets, &model_availability_nux_config(&[]));
|
||||
|
||||
assert_eq!(
|
||||
selected,
|
||||
Some(StartupTooltipOverride {
|
||||
model_slug: "gpt-5.4".to_string(),
|
||||
message: "second".to_string(),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn select_model_availability_nux_returns_none_when_all_models_are_exhausted() {
|
||||
let mut presets = all_model_presets();
|
||||
presets.iter_mut().for_each(|preset| {
|
||||
preset.availability_nux = None;
|
||||
});
|
||||
let target = presets
|
||||
.iter_mut()
|
||||
.find(|preset| preset.model == "gpt-5.4")
|
||||
.expect("target preset present");
|
||||
target.availability_nux = Some(ModelAvailabilityNux {
|
||||
message: "gpt-5.4 is available".to_string(),
|
||||
});
|
||||
|
||||
let selected = select_model_availability_nux(
|
||||
&presets,
|
||||
&model_availability_nux_config(&[("gpt-5.4", MODEL_AVAILABILITY_NUX_MAX_SHOW_COUNT)]),
|
||||
);
|
||||
|
||||
assert_eq!(selected, None);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn model_migration_prompt_respects_hide_flag_and_self_target() {
|
||||
let mut seen = BTreeMap::new();
|
||||
seen.insert("gpt-5.2".to_string(), "gpt-5.4".to_string());
|
||||
assert!(!should_show_model_migration_prompt(
|
||||
"gpt-5.2",
|
||||
"gpt-5.4",
|
||||
&seen,
|
||||
&all_model_presets()
|
||||
));
|
||||
assert!(!should_show_model_migration_prompt(
|
||||
"gpt-5.4",
|
||||
"gpt-5.4",
|
||||
&seen,
|
||||
&all_model_presets()
|
||||
));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn model_migration_prompt_skips_when_target_missing_or_hidden() {
|
||||
let mut available = all_model_presets();
|
||||
let mut current = available
|
||||
.iter()
|
||||
.find(|preset| preset.model == "gpt-5.2")
|
||||
.cloned()
|
||||
.expect("preset present");
|
||||
current.upgrade = Some(ModelUpgrade {
|
||||
id: "missing-target".to_string(),
|
||||
reasoning_effort_mapping: None,
|
||||
migration_config_key: HIDE_GPT5_1_MIGRATION_PROMPT_CONFIG.to_string(),
|
||||
model_link: None,
|
||||
upgrade_copy: None,
|
||||
migration_markdown: None,
|
||||
});
|
||||
available.retain(|preset| preset.model != "gpt-5.2");
|
||||
available.push(current.clone());
|
||||
|
||||
assert!(!should_show_model_migration_prompt(
|
||||
¤t.model,
|
||||
"missing-target",
|
||||
&BTreeMap::new(),
|
||||
&available,
|
||||
));
|
||||
|
||||
assert!(target_preset_for_upgrade(&available, "missing-target").is_none());
|
||||
|
||||
let mut with_hidden_target = all_model_presets();
|
||||
let target = with_hidden_target
|
||||
.iter_mut()
|
||||
.find(|preset| preset.model == "gpt-5.4")
|
||||
.expect("target preset present");
|
||||
target.show_in_picker = false;
|
||||
|
||||
assert!(!should_show_model_migration_prompt(
|
||||
"gpt-5.2",
|
||||
"gpt-5.4",
|
||||
&BTreeMap::new(),
|
||||
&with_hidden_target,
|
||||
));
|
||||
assert!(target_preset_for_upgrade(&with_hidden_target, "gpt-5.4").is_none());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn model_migration_prompt_shows_for_hidden_model() {
|
||||
let codex_home = tempdir().expect("temp codex home");
|
||||
let config = ConfigBuilder::default()
|
||||
.codex_home(codex_home.path().to_path_buf())
|
||||
.build()
|
||||
.await
|
||||
.expect("config");
|
||||
|
||||
let mut available_models = all_model_presets();
|
||||
let current = available_models
|
||||
.iter_mut()
|
||||
.find(|preset| preset.model == "gpt-5.3-codex")
|
||||
.expect("gpt-5.3-codex preset present");
|
||||
current.show_in_picker = false;
|
||||
let current = current.clone();
|
||||
assert!(
|
||||
!current.show_in_picker,
|
||||
"expected gpt-5.3-codex to be hidden from picker for this test"
|
||||
);
|
||||
|
||||
let upgrade = current.upgrade.as_ref().expect("upgrade configured");
|
||||
available_models
|
||||
.iter_mut()
|
||||
.find(|preset| preset.model == upgrade.id)
|
||||
.expect("upgrade target present")
|
||||
.show_in_picker = true;
|
||||
assert!(
|
||||
should_show_model_migration_prompt(
|
||||
¤t.model,
|
||||
&upgrade.id,
|
||||
&config.notices.model_migrations,
|
||||
&available_models,
|
||||
),
|
||||
"expected migration prompt to be eligible for hidden model"
|
||||
);
|
||||
|
||||
let target =
|
||||
target_preset_for_upgrade(&available_models, &upgrade.id).expect("upgrade target present");
|
||||
let target_description = (!target.description.is_empty()).then(|| target.description.clone());
|
||||
let can_opt_out = true;
|
||||
let copy = migration_copy_for_models(
|
||||
¤t.model,
|
||||
&upgrade.id,
|
||||
upgrade.model_link.clone(),
|
||||
upgrade.upgrade_copy.clone(),
|
||||
upgrade.migration_markdown.clone(),
|
||||
target.display_name.clone(),
|
||||
target_description,
|
||||
can_opt_out,
|
||||
);
|
||||
|
||||
assert_snapshot!(
|
||||
"model_migration_prompt_shows_for_hidden_model",
|
||||
model_migration_copy_to_plain_text(©)
|
||||
);
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
---
|
||||
source: tui/src/app/tests/model_catalog.rs
|
||||
assertion_line: 289
|
||||
expression: model_migration_copy_to_plain_text(©)
|
||||
---
|
||||
Introducing GPT-5.4
|
||||
|
||||
Codex just got an upgrade with GPT-5.4, our most capable model for professional work. It outperforms prior models while being more token efficient, with notable improvements on long-running tasks, tool calling, computer use, and frontend development.
|
||||
|
||||
Learn more: https://openai.com/index/introducing-gpt-5-4
|
||||
|
||||
You can always keep using GPT-5.3-Codex if you prefer.
|
||||
+1
-1
@@ -10,4 +10,4 @@ expression: normalize_snapshot_paths(term.backend().vt100().screen().contents())
|
||||
|
||||
› Ask Codex to do anything
|
||||
|
||||
gpt-5.3-codex default · /tmp/project
|
||||
gpt-5.4 default · /tmp/project
|
||||
|
||||
+1
-1
@@ -17,4 +17,4 @@ expression: normalize_snapshot_paths(term.backend().vt100().screen().contents())
|
||||
|
||||
› Ask Codex to do anything
|
||||
|
||||
gpt-5.3-codex default · /tmp/project
|
||||
gpt-5.4 default · /tmp/project
|
||||
|
||||
+1
-1
@@ -8,4 +8,4 @@ expression: normalize_snapshot_paths(term.backend().vt100().screen().contents())
|
||||
|
||||
› Ask Codex to do anything
|
||||
|
||||
gpt-5.3-codex default · /tmp/project
|
||||
gpt-5.4 default · /tmp/project
|
||||
|
||||
+1
-1
@@ -26,4 +26,4 @@ expression: normalize_snapshot_paths(term.backend().vt100().screen().contents())
|
||||
|
||||
› Ask Codex to do anything
|
||||
|
||||
gpt-5.3-codex default · /tmp/project
|
||||
gpt-5.4 default · /tmp/project
|
||||
|
||||
+1
-1
@@ -9,4 +9,4 @@ expression: normalize_snapshot_paths(term.backend().vt100().screen().contents())
|
||||
|
||||
› Ask Codex to do anything
|
||||
|
||||
gpt-5.3-codex default · /tmp/project
|
||||
gpt-5.4 default · /tmp/project
|
||||
|
||||
+1
-1
@@ -8,4 +8,4 @@ expression: normalize_snapshot_paths(term.backend().vt100().screen().contents())
|
||||
|
||||
› Ask Codex to do anything
|
||||
|
||||
gpt-5.3-codex default · /tmp/project
|
||||
gpt-5.4 default · /tmp/project
|
||||
|
||||
+1
-1
@@ -14,4 +14,4 @@ expression: normalize_snapshot_paths(term.backend().vt100().screen().contents())
|
||||
|
||||
› Ask Codex to do anything
|
||||
|
||||
gpt-5.3-codex default · /tmp/project
|
||||
gpt-5.4 default · /tmp/project
|
||||
|
||||
+1
-1
@@ -9,4 +9,4 @@ expression: normalize_snapshot_paths(rendered)
|
||||
|
||||
› Ask Codex to do anything
|
||||
|
||||
gpt-5.3-codex default · /tmp/project
|
||||
gpt-5.4 default · /tmp/project
|
||||
|
||||
+1
-1
@@ -21,4 +21,4 @@ expression: normalize_snapshot_paths(term.backend().vt100().screen().contents())
|
||||
|
||||
› Ask Codex to do anything
|
||||
|
||||
gpt-5.3-codex default · /tmp/project
|
||||
gpt-5.4 default · /tmp/project
|
||||
|
||||
+1
-1
@@ -8,4 +8,4 @@ expression: normalized_backend_snapshot(terminal.backend())
|
||||
" "
|
||||
"› Ask Codex to do anything "
|
||||
" "
|
||||
" gpt-5.3-codex default · /tmp/project "
|
||||
" gpt-5.4 default · /tmp/project "
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
source: tui/src/chatwidget/tests.rs
|
||||
expression: popup
|
||||
---
|
||||
Select Reasoning Level for gpt-5.1-codex-max
|
||||
Select Reasoning Level for gpt-5.4
|
||||
|
||||
1. Low Fast responses with lighter reasoning
|
||||
2. Medium (default) Balances speed and reasoning depth for everyday tasks
|
||||
|
||||
+9
-7
@@ -1,14 +1,16 @@
|
||||
---
|
||||
source: tui/src/chatwidget/tests.rs
|
||||
source: tui/src/chatwidget/tests/popups_and_settings.rs
|
||||
expression: popup
|
||||
---
|
||||
Select Reasoning Level for gpt-5.1-codex-max
|
||||
Select Reasoning Level for gpt-5.2
|
||||
|
||||
1. Low Fast responses with lighter reasoning
|
||||
2. Medium (default) Balances speed and reasoning depth for everyday
|
||||
tasks
|
||||
3. High Greater reasoning depth for complex problems
|
||||
› 4. Extra high (current) Extra high reasoning depth for complex problems
|
||||
1. Low Balances speed with some reasoning; useful for
|
||||
straightforward queries and short explanations
|
||||
2. Medium (default) Provides a solid balance of reasoning depth and
|
||||
latency for general-purpose tasks
|
||||
3. High Maximizes reasoning depth for complex or ambiguous
|
||||
problems
|
||||
› 4. Extra high (current) Extra high reasoning for complex problems
|
||||
⚠ Extra high reasoning effort can quickly consume
|
||||
Plus plan rate limits.
|
||||
|
||||
|
||||
+6
-10
@@ -1,18 +1,14 @@
|
||||
---
|
||||
source: tui/src/chatwidget/tests.rs
|
||||
source: tui/src/chatwidget/tests/popups_and_settings.rs
|
||||
expression: popup
|
||||
---
|
||||
Select Model and Effort
|
||||
Access legacy models by running codex -m <model_name> or in your config.toml
|
||||
|
||||
› 1. gpt-5.3-codex (default) Latest frontier agentic coding model.
|
||||
2. gpt-5.4 Latest frontier agentic coding model.
|
||||
3. gpt-5.2-codex Frontier agentic coding model.
|
||||
4. gpt-5.1-codex-max Codex-optimized flagship for deep and fast
|
||||
reasoning.
|
||||
5. gpt-5.2 Latest frontier model with improvements across
|
||||
knowledge, reasoning and coding
|
||||
6. gpt-5.1-codex-mini Optimized for codex. Cheaper, faster, but less
|
||||
capable.
|
||||
1. gpt-5.4 (default) Latest frontier agentic coding model.
|
||||
2. gpt-5.4-mini Smaller frontier agentic coding model.
|
||||
3. gpt-5.3-codex Frontier Codex-optimized agentic coding model.
|
||||
› 4. gpt-5.2 (current) Optimized for professional work and long-running
|
||||
agents
|
||||
|
||||
Press enter to select reasoning effort, or esc to dismiss.
|
||||
|
||||
+1
-1
@@ -8,4 +8,4 @@ expression: normalized_backend_snapshot(terminal.backend())
|
||||
" "
|
||||
"› Ask Codex to do anything "
|
||||
" "
|
||||
" gpt-5.3-codex default · /tmp/project "
|
||||
" gpt-5.4 default · /tmp/project "
|
||||
|
||||
+3
-3
@@ -3,10 +3,10 @@ source: tui/src/chatwidget/tests.rs
|
||||
expression: popup
|
||||
---
|
||||
Approaching rate limits
|
||||
Switch to gpt-5.1-codex-mini for lower credit usage?
|
||||
Switch to gpt-5.4-mini for lower credit usage?
|
||||
|
||||
› 1. Switch to gpt-5.1-codex-mini Optimized for codex. Cheaper,
|
||||
faster, but less capable.
|
||||
› 1. Switch to gpt-5.4-mini Smaller frontier agentic coding
|
||||
model.
|
||||
2. Keep current model
|
||||
3. Keep current model (never show again) Hide future rate limit reminders
|
||||
about switching models.
|
||||
|
||||
+1
-1
@@ -9,4 +9,4 @@ expression: normalize_snapshot_paths(term.backend().vt100().screen().contents())
|
||||
|
||||
› Ask Codex to do anything
|
||||
|
||||
gpt-5.3-codex default · /tmp/project
|
||||
gpt-5.4 default · /tmp/project
|
||||
|
||||
+1
-1
@@ -6,4 +6,4 @@ expression: terminal.backend()
|
||||
" "
|
||||
"› Check recently modified functions for compatibility "
|
||||
" "
|
||||
" gpt-5.3-codex Side from main thread · Esc to return "
|
||||
" gpt-5.4 Side from main thread · Esc to return "
|
||||
|
||||
+1
-1
@@ -6,4 +6,4 @@ expression: terminal.backend()
|
||||
" "
|
||||
"› Check recently modified functions for compatibility "
|
||||
" "
|
||||
" gpt-5.3-codex defa… Side from main thread · main needs input · Esc to return "
|
||||
" gpt-5.4 default · … Side from main thread · main needs input · Esc to return "
|
||||
|
||||
+1
-1
@@ -8,4 +8,4 @@ expression: normalized_backend_snapshot(terminal.backend())
|
||||
" "
|
||||
"› Ask Codex to do anything "
|
||||
" "
|
||||
" gpt-5.3-codex default Side starting... "
|
||||
" gpt-5.4 default Side starting... "
|
||||
|
||||
+1
-1
@@ -8,4 +8,4 @@ expression: normalized_backend_snapshot(terminal.backend())
|
||||
" "
|
||||
"› Ask Codex to do anything "
|
||||
" "
|
||||
" gpt-5.3-codex default · /tmp/project "
|
||||
" gpt-5.4 default · /tmp/project "
|
||||
|
||||
+1
-1
@@ -8,4 +8,4 @@ expression: normalized_backend_snapshot(terminal.backend())
|
||||
" "
|
||||
"› Ask Codex to do anything "
|
||||
" "
|
||||
" gpt-5.3-codex default · /tmp/project "
|
||||
" gpt-5.4 default · /tmp/project "
|
||||
|
||||
+1
-1
@@ -8,4 +8,4 @@ expression: normalize_snapshot_paths(rendered)
|
||||
|
||||
› Ask Codex to do anything
|
||||
|
||||
gpt-5.3-codex default · /tmp/project
|
||||
gpt-5.4 default · /tmp/project
|
||||
|
||||
@@ -169,7 +169,7 @@ async fn submit_user_message_with_mode_sets_coding_collaboration_mode() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn reasoning_selection_in_plan_mode_opens_scope_prompt_event() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.1-codex-max")).await;
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.4")).await;
|
||||
chat.thread_id = Some(ThreadId::new());
|
||||
chat.set_feature_enabled(Feature::CollaborationModes, /*enabled*/ true);
|
||||
let plan_mask = collaboration_modes::plan_mask(chat.model_catalog.as_ref())
|
||||
@@ -179,7 +179,7 @@ async fn reasoning_selection_in_plan_mode_opens_scope_prompt_event() {
|
||||
set_chatgpt_auth(&mut chat);
|
||||
chat.set_reasoning_effort(Some(ReasoningEffortConfig::High));
|
||||
|
||||
let preset = get_available_model(&chat, "gpt-5.1-codex-max");
|
||||
let preset = get_available_model(&chat, "gpt-5.4");
|
||||
chat.open_reasoning_popup(preset);
|
||||
chat.handle_key_event(KeyEvent::from(KeyCode::Down));
|
||||
chat.handle_key_event(KeyEvent::from(KeyCode::Enter));
|
||||
@@ -190,13 +190,13 @@ async fn reasoning_selection_in_plan_mode_opens_scope_prompt_event() {
|
||||
AppEvent::OpenPlanReasoningScopePrompt {
|
||||
model,
|
||||
effort: Some(_)
|
||||
} if model == "gpt-5.1-codex-max"
|
||||
} if model == "gpt-5.4"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn reasoning_selection_in_plan_mode_without_effort_change_does_not_open_scope_prompt_event() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.1-codex-max")).await;
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.4")).await;
|
||||
chat.thread_id = Some(ThreadId::new());
|
||||
chat.set_feature_enabled(Feature::CollaborationModes, /*enabled*/ true);
|
||||
let plan_mask = collaboration_modes::plan_mask(chat.model_catalog.as_ref())
|
||||
@@ -205,10 +205,10 @@ async fn reasoning_selection_in_plan_mode_without_effort_change_does_not_open_sc
|
||||
let _ = drain_insert_history(&mut rx);
|
||||
set_chatgpt_auth(&mut chat);
|
||||
|
||||
let current_preset = get_available_model(&chat, "gpt-5.1-codex-max");
|
||||
let current_preset = get_available_model(&chat, "gpt-5.4");
|
||||
chat.set_reasoning_effort(Some(current_preset.default_reasoning_effort));
|
||||
|
||||
let preset = get_available_model(&chat, "gpt-5.1-codex-max");
|
||||
let preset = get_available_model(&chat, "gpt-5.4");
|
||||
chat.open_reasoning_popup(preset);
|
||||
chat.handle_key_event(KeyEvent::from(KeyCode::Enter));
|
||||
|
||||
@@ -216,7 +216,7 @@ async fn reasoning_selection_in_plan_mode_without_effort_change_does_not_open_sc
|
||||
assert!(
|
||||
events.iter().any(|event| matches!(
|
||||
event,
|
||||
AppEvent::UpdateModel(model) if model == "gpt-5.1-codex-max"
|
||||
AppEvent::UpdateModel(model) if model == "gpt-5.4"
|
||||
)),
|
||||
"expected model update event; events: {events:?}"
|
||||
);
|
||||
@@ -231,7 +231,7 @@ async fn reasoning_selection_in_plan_mode_without_effort_change_does_not_open_sc
|
||||
#[tokio::test]
|
||||
async fn reasoning_selection_in_plan_mode_matching_plan_effort_but_different_global_opens_scope_prompt()
|
||||
{
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.1-codex-max")).await;
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.4")).await;
|
||||
chat.thread_id = Some(ThreadId::new());
|
||||
chat.set_feature_enabled(Feature::CollaborationModes, /*enabled*/ true);
|
||||
let plan_mask = collaboration_modes::plan_mask(chat.model_catalog.as_ref())
|
||||
@@ -245,7 +245,7 @@ async fn reasoning_selection_in_plan_mode_matching_plan_effort_but_different_glo
|
||||
// should open the scope prompt rather than silently rewriting the global default.
|
||||
chat.set_reasoning_effort(Some(ReasoningEffortConfig::High));
|
||||
|
||||
let preset = get_available_model(&chat, "gpt-5.1-codex-max");
|
||||
let preset = get_available_model(&chat, "gpt-5.4");
|
||||
chat.open_reasoning_popup(preset);
|
||||
chat.handle_key_event(KeyEvent::from(KeyCode::Enter));
|
||||
|
||||
@@ -255,13 +255,13 @@ async fn reasoning_selection_in_plan_mode_matching_plan_effort_but_different_glo
|
||||
AppEvent::OpenPlanReasoningScopePrompt {
|
||||
model,
|
||||
effort: Some(ReasoningEffortConfig::Medium)
|
||||
} if model == "gpt-5.1-codex-max"
|
||||
} if model == "gpt-5.4"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn plan_mode_reasoning_override_is_marked_current_in_reasoning_popup() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.1-codex-max")).await;
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.4")).await;
|
||||
chat.set_feature_enabled(Feature::CollaborationModes, /*enabled*/ true);
|
||||
set_chatgpt_auth(&mut chat);
|
||||
chat.set_reasoning_effort(Some(ReasoningEffortConfig::High));
|
||||
@@ -271,7 +271,7 @@ async fn plan_mode_reasoning_override_is_marked_current_in_reasoning_popup() {
|
||||
.expect("expected plan collaboration mode");
|
||||
chat.set_collaboration_mask(plan_mask);
|
||||
|
||||
let preset = get_available_model(&chat, "gpt-5.1-codex-max");
|
||||
let preset = get_available_model(&chat, "gpt-5.4");
|
||||
chat.open_reasoning_popup(preset);
|
||||
|
||||
let popup = render_bottom_popup(&chat, /*width*/ 100);
|
||||
@@ -284,7 +284,7 @@ async fn plan_mode_reasoning_override_is_marked_current_in_reasoning_popup() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn reasoning_selection_in_plan_mode_model_switch_does_not_open_scope_prompt_event() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.1-codex-max")).await;
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.4")).await;
|
||||
chat.thread_id = Some(ThreadId::new());
|
||||
chat.set_feature_enabled(Feature::CollaborationModes, /*enabled*/ true);
|
||||
let plan_mask = collaboration_modes::plan_mask(chat.model_catalog.as_ref())
|
||||
@@ -293,7 +293,7 @@ async fn reasoning_selection_in_plan_mode_model_switch_does_not_open_scope_promp
|
||||
let _ = drain_insert_history(&mut rx);
|
||||
set_chatgpt_auth(&mut chat);
|
||||
|
||||
let preset = get_available_model(&chat, "gpt-5");
|
||||
let preset = get_available_model(&chat, "gpt-5.2");
|
||||
chat.open_reasoning_popup(preset);
|
||||
chat.handle_key_event(KeyEvent::from(KeyCode::Enter));
|
||||
|
||||
@@ -301,7 +301,7 @@ async fn reasoning_selection_in_plan_mode_model_switch_does_not_open_scope_promp
|
||||
assert!(
|
||||
events.iter().any(|event| matches!(
|
||||
event,
|
||||
AppEvent::UpdateModel(model) if model == "gpt-5"
|
||||
AppEvent::UpdateModel(model) if model == "gpt-5.2"
|
||||
)),
|
||||
"expected model update event; events: {events:?}"
|
||||
);
|
||||
@@ -315,11 +315,8 @@ async fn reasoning_selection_in_plan_mode_model_switch_does_not_open_scope_promp
|
||||
|
||||
#[tokio::test]
|
||||
async fn plan_reasoning_scope_popup_all_modes_persists_global_and_plan_override() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.1-codex-max")).await;
|
||||
chat.open_plan_reasoning_scope_prompt(
|
||||
"gpt-5.1-codex-max".to_string(),
|
||||
Some(ReasoningEffortConfig::High),
|
||||
);
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.4")).await;
|
||||
chat.open_plan_reasoning_scope_prompt("gpt-5.4".to_string(), Some(ReasoningEffortConfig::High));
|
||||
|
||||
chat.handle_key_event(KeyEvent::from(KeyCode::Down));
|
||||
chat.handle_key_event(KeyEvent::from(KeyCode::Enter));
|
||||
@@ -343,7 +340,7 @@ async fn plan_reasoning_scope_popup_all_modes_persists_global_and_plan_override(
|
||||
events.iter().any(|event| matches!(
|
||||
event,
|
||||
AppEvent::PersistModelSelection { model, effort: Some(ReasoningEffortConfig::High) }
|
||||
if model == "gpt-5.1-codex-max"
|
||||
if model == "gpt-5.4"
|
||||
)),
|
||||
"expected global model reasoning selection persistence; events: {events:?}"
|
||||
);
|
||||
@@ -369,7 +366,7 @@ fn plan_mode_prompt_notification_uses_dedicated_type_name() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn open_plan_implementation_prompt_sets_pending_notification() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.1-codex-max")).await;
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.4")).await;
|
||||
chat.config.tui_notifications.notifications =
|
||||
Notifications::Custom(vec!["plan-mode-prompt".to_string()]);
|
||||
|
||||
@@ -383,14 +380,11 @@ async fn open_plan_implementation_prompt_sets_pending_notification() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn open_plan_reasoning_scope_prompt_sets_pending_notification() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.1-codex-max")).await;
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.4")).await;
|
||||
chat.config.tui_notifications.notifications =
|
||||
Notifications::Custom(vec!["plan-mode-prompt".to_string()]);
|
||||
|
||||
chat.open_plan_reasoning_scope_prompt(
|
||||
"gpt-5.1-codex-max".to_string(),
|
||||
Some(ReasoningEffortConfig::High),
|
||||
);
|
||||
chat.open_plan_reasoning_scope_prompt("gpt-5.4".to_string(), Some(ReasoningEffortConfig::High));
|
||||
|
||||
assert_matches!(
|
||||
chat.pending_notification,
|
||||
@@ -400,7 +394,7 @@ async fn open_plan_reasoning_scope_prompt_sets_pending_notification() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn agent_turn_complete_does_not_override_pending_plan_mode_prompt_notification() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.1-codex-max")).await;
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.4")).await;
|
||||
|
||||
chat.open_plan_implementation_prompt();
|
||||
chat.notify(Notification::AgentTurnComplete {
|
||||
@@ -415,7 +409,7 @@ async fn agent_turn_complete_does_not_override_pending_plan_mode_prompt_notifica
|
||||
|
||||
#[tokio::test]
|
||||
async fn request_user_input_notification_overrides_pending_agent_turn_complete_notification() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.1-codex-max")).await;
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.4")).await;
|
||||
|
||||
chat.notify(Notification::AgentTurnComplete {
|
||||
response: "done".to_string(),
|
||||
@@ -444,7 +438,7 @@ async fn request_user_input_notification_overrides_pending_agent_turn_complete_n
|
||||
|
||||
#[tokio::test]
|
||||
async fn handle_request_user_input_sets_pending_notification() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.1-codex-max")).await;
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.4")).await;
|
||||
chat.config.tui_notifications.notifications =
|
||||
Notifications::Custom(vec!["plan-mode-prompt".to_string()]);
|
||||
|
||||
@@ -472,10 +466,10 @@ async fn handle_request_user_input_sets_pending_notification() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn plan_reasoning_scope_popup_mentions_selected_reasoning() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.1-codex-max")).await;
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.4")).await;
|
||||
chat.set_plan_mode_reasoning_effort(Some(ReasoningEffortConfig::Low));
|
||||
chat.open_plan_reasoning_scope_prompt(
|
||||
"gpt-5.1-codex-max".to_string(),
|
||||
"gpt-5.4".to_string(),
|
||||
Some(ReasoningEffortConfig::Medium),
|
||||
);
|
||||
|
||||
@@ -489,9 +483,9 @@ async fn plan_reasoning_scope_popup_mentions_selected_reasoning() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn plan_reasoning_scope_popup_mentions_built_in_plan_default_when_no_override() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.1-codex-max")).await;
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.4")).await;
|
||||
chat.open_plan_reasoning_scope_prompt(
|
||||
"gpt-5.1-codex-max".to_string(),
|
||||
"gpt-5.4".to_string(),
|
||||
Some(ReasoningEffortConfig::Medium),
|
||||
);
|
||||
|
||||
@@ -501,11 +495,8 @@ async fn plan_reasoning_scope_popup_mentions_built_in_plan_default_when_no_overr
|
||||
|
||||
#[tokio::test]
|
||||
async fn plan_reasoning_scope_popup_plan_only_does_not_update_all_modes_reasoning() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.1-codex-max")).await;
|
||||
chat.open_plan_reasoning_scope_prompt(
|
||||
"gpt-5.1-codex-max".to_string(),
|
||||
Some(ReasoningEffortConfig::High),
|
||||
);
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.4")).await;
|
||||
chat.open_plan_reasoning_scope_prompt("gpt-5.4".to_string(), Some(ReasoningEffortConfig::High));
|
||||
|
||||
chat.handle_key_event(KeyEvent::from(KeyCode::Enter));
|
||||
|
||||
@@ -1123,7 +1114,7 @@ async fn mode_switch_surfaces_model_change_notification_when_effective_model_cha
|
||||
let mut plan_mask =
|
||||
collaboration_modes::mask_for_kind(chat.model_catalog.as_ref(), ModeKind::Plan)
|
||||
.expect("expected plan collaboration mode");
|
||||
plan_mask.model = Some("gpt-5.1-codex-mini".to_string());
|
||||
plan_mask.model = Some("gpt-5.4-mini".to_string());
|
||||
chat.set_collaboration_mask(plan_mask);
|
||||
|
||||
let plan_messages = drain_insert_history(&mut rx)
|
||||
@@ -1132,7 +1123,7 @@ async fn mode_switch_surfaces_model_change_notification_when_effective_model_cha
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
assert!(
|
||||
plan_messages.contains("Model changed to gpt-5.1-codex-mini medium for Plan mode."),
|
||||
plan_messages.contains("Model changed to gpt-5.4-mini medium for Plan mode."),
|
||||
"expected Plan-mode model switch notice, got: {plan_messages:?}"
|
||||
);
|
||||
|
||||
@@ -1336,21 +1327,21 @@ async fn collaboration_modes_defaults_to_code_on_startup() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn set_model_updates_active_collaboration_mask() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.1")).await;
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.2")).await;
|
||||
chat.set_feature_enabled(Feature::CollaborationModes, /*enabled*/ true);
|
||||
let plan_mask = collaboration_modes::mask_for_kind(chat.model_catalog.as_ref(), ModeKind::Plan)
|
||||
.expect("expected plan collaboration mask");
|
||||
chat.set_collaboration_mask(plan_mask);
|
||||
|
||||
chat.set_model("gpt-5.1-codex-mini");
|
||||
chat.set_model("gpt-5.4-mini");
|
||||
|
||||
assert_eq!(chat.current_model(), "gpt-5.1-codex-mini");
|
||||
assert_eq!(chat.current_model(), "gpt-5.4-mini");
|
||||
assert_eq!(chat.active_collaboration_mode_kind(), ModeKind::Plan);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn set_reasoning_effort_updates_active_collaboration_mask() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.1")).await;
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.2")).await;
|
||||
chat.set_feature_enabled(Feature::CollaborationModes, /*enabled*/ true);
|
||||
let plan_mask = collaboration_modes::mask_for_kind(chat.model_catalog.as_ref(), ModeKind::Plan)
|
||||
.expect("expected plan collaboration mask");
|
||||
@@ -1367,7 +1358,7 @@ async fn set_reasoning_effort_updates_active_collaboration_mask() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn set_reasoning_effort_does_not_override_active_plan_override() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.1")).await;
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.2")).await;
|
||||
chat.set_feature_enabled(Feature::CollaborationModes, /*enabled*/ true);
|
||||
chat.set_plan_mode_reasoning_effort(Some(ReasoningEffortConfig::High));
|
||||
let plan_mask = collaboration_modes::mask_for_kind(chat.model_catalog.as_ref(), ModeKind::Plan)
|
||||
@@ -1437,10 +1428,10 @@ async fn collab_mode_applies_default_preset() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn user_turn_includes_personality_from_config() {
|
||||
let (mut chat, _rx, mut op_rx) = make_chatwidget_manual(Some("gpt-5.2-codex")).await;
|
||||
let (mut chat, _rx, mut op_rx) = make_chatwidget_manual(Some("gpt-5.3-codex")).await;
|
||||
chat.set_feature_enabled(Feature::Personality, /*enabled*/ true);
|
||||
chat.thread_id = Some(ThreadId::new());
|
||||
chat.set_model("gpt-5.2-codex");
|
||||
chat.set_model("gpt-5.3-codex");
|
||||
chat.set_personality(Personality::Friendly);
|
||||
|
||||
chat.bottom_pane
|
||||
|
||||
@@ -1901,7 +1901,7 @@ async fn memories_reset_confirmation_sends_event_on_confirm() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn model_selection_popup_snapshot() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5-codex")).await;
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.2")).await;
|
||||
chat.thread_id = Some(ThreadId::new());
|
||||
chat.open_model_popup();
|
||||
|
||||
@@ -1911,7 +1911,7 @@ async fn model_selection_popup_snapshot() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn personality_selection_popup_snapshot() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.2-codex")).await;
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.3-codex")).await;
|
||||
chat.thread_id = Some(ThreadId::new());
|
||||
chat.open_personality_popup();
|
||||
|
||||
@@ -1922,7 +1922,7 @@ async fn personality_selection_popup_snapshot() {
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
#[tokio::test]
|
||||
async fn realtime_audio_selection_popup_snapshot() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.2-codex")).await;
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.3-codex")).await;
|
||||
chat.open_realtime_audio_popup();
|
||||
|
||||
let popup = render_bottom_popup(&chat, /*width*/ 80);
|
||||
@@ -1932,7 +1932,7 @@ async fn realtime_audio_selection_popup_snapshot() {
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
#[tokio::test]
|
||||
async fn realtime_audio_selection_popup_narrow_snapshot() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.2-codex")).await;
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.3-codex")).await;
|
||||
chat.open_realtime_audio_popup();
|
||||
|
||||
let popup = render_bottom_popup(&chat, /*width*/ 56);
|
||||
@@ -1942,7 +1942,7 @@ async fn realtime_audio_selection_popup_narrow_snapshot() {
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
#[tokio::test]
|
||||
async fn realtime_microphone_picker_popup_snapshot() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.2-codex")).await;
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.3-codex")).await;
|
||||
chat.config.realtime_audio.microphone = Some("Studio Mic".to_string());
|
||||
chat.open_realtime_audio_device_selection_with_names(
|
||||
RealtimeAudioDeviceKind::Microphone,
|
||||
@@ -1956,7 +1956,7 @@ async fn realtime_microphone_picker_popup_snapshot() {
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
#[tokio::test]
|
||||
async fn realtime_audio_picker_emits_persist_event() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.2-codex")).await;
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.3-codex")).await;
|
||||
chat.open_realtime_audio_device_selection_with_names(
|
||||
RealtimeAudioDeviceKind::Speaker,
|
||||
vec!["Desk Speakers".to_string(), "Headphones".to_string()],
|
||||
@@ -2017,8 +2017,8 @@ async fn model_picker_hides_show_in_picker_false_models_from_cache() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn server_overloaded_error_does_not_switch_models() {
|
||||
let (mut chat, mut rx, mut op_rx) = make_chatwidget_manual(Some("gpt-5.2-codex")).await;
|
||||
chat.set_model("gpt-5.2-codex");
|
||||
let (mut chat, mut rx, mut op_rx) = make_chatwidget_manual(Some("gpt-5.3-codex")).await;
|
||||
chat.set_model("gpt-5.3-codex");
|
||||
while rx.try_recv().is_ok() {}
|
||||
while op_rx.try_recv().is_ok() {}
|
||||
|
||||
@@ -2033,7 +2033,7 @@ async fn server_overloaded_error_does_not_switch_models() {
|
||||
while let Ok(event) = rx.try_recv() {
|
||||
if let AppEvent::UpdateModel(model) = event {
|
||||
assert_eq!(
|
||||
model, "gpt-5.2-codex",
|
||||
model, "gpt-5.3-codex",
|
||||
"did not expect model switch on server-overloaded error"
|
||||
);
|
||||
}
|
||||
@@ -2051,12 +2051,12 @@ async fn server_overloaded_error_does_not_switch_models() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn model_reasoning_selection_popup_snapshot() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.1-codex-max")).await;
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.4")).await;
|
||||
|
||||
set_chatgpt_auth(&mut chat);
|
||||
chat.set_reasoning_effort(Some(ReasoningEffortConfig::High));
|
||||
|
||||
let preset = get_available_model(&chat, "gpt-5.1-codex-max");
|
||||
let preset = get_available_model(&chat, "gpt-5.4");
|
||||
chat.open_reasoning_popup(preset);
|
||||
|
||||
let popup = render_bottom_popup(&chat, /*width*/ 80);
|
||||
@@ -2065,12 +2065,12 @@ async fn model_reasoning_selection_popup_snapshot() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn model_reasoning_selection_popup_extra_high_warning_snapshot() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.1-codex-max")).await;
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.2")).await;
|
||||
|
||||
set_chatgpt_auth(&mut chat);
|
||||
chat.set_reasoning_effort(Some(ReasoningEffortConfig::XHigh));
|
||||
|
||||
let preset = get_available_model(&chat, "gpt-5.1-codex-max");
|
||||
let preset = get_available_model(&chat, "gpt-5.2");
|
||||
chat.open_reasoning_popup(preset);
|
||||
|
||||
let popup = render_bottom_popup(&chat, /*width*/ 80);
|
||||
@@ -2079,11 +2079,11 @@ async fn model_reasoning_selection_popup_extra_high_warning_snapshot() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn reasoning_popup_shows_extra_high_with_space() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.1-codex-max")).await;
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.4")).await;
|
||||
|
||||
set_chatgpt_auth(&mut chat);
|
||||
|
||||
let preset = get_available_model(&chat, "gpt-5.1-codex-max");
|
||||
let preset = get_available_model(&chat, "gpt-5.4");
|
||||
chat.open_reasoning_popup(preset);
|
||||
|
||||
let popup = render_bottom_popup(&chat, /*width*/ 120);
|
||||
@@ -2193,11 +2193,11 @@ async fn feedback_good_result_consent_popup_includes_connectivity_diagnostics_fi
|
||||
|
||||
#[tokio::test]
|
||||
async fn reasoning_popup_escape_returns_to_model_popup() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.1-codex-max")).await;
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.4")).await;
|
||||
chat.thread_id = Some(ThreadId::new());
|
||||
chat.open_model_popup();
|
||||
|
||||
let preset = get_available_model(&chat, "gpt-5.1-codex-max");
|
||||
let preset = get_available_model(&chat, "gpt-5.4");
|
||||
chat.open_reasoning_popup(preset);
|
||||
|
||||
let before_escape = render_bottom_popup(&chat, /*width*/ 80);
|
||||
|
||||
@@ -299,7 +299,7 @@ async fn queued_bang_shell_waits_for_user_shell_completion_before_next_input() {
|
||||
}
|
||||
|
||||
async fn assert_cancelled_queued_menu_drains_next_input(command: &str, expected_popup_text: &str) {
|
||||
let (mut chat, _rx, mut op_rx) = make_chatwidget_manual(Some("gpt-5-codex")).await;
|
||||
let (mut chat, _rx, mut op_rx) = make_chatwidget_manual(Some("gpt-5.2")).await;
|
||||
chat.thread_id = Some(ThreadId::new());
|
||||
chat.handle_codex_event(Event {
|
||||
id: "turn-start".into(),
|
||||
@@ -351,7 +351,7 @@ async fn queued_slash_menu_cancel_drains_next_input() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn queued_slash_menu_selection_drains_next_input() {
|
||||
let (mut chat, _rx, mut op_rx) = make_chatwidget_manual(Some("gpt-5-codex")).await;
|
||||
let (mut chat, _rx, mut op_rx) = make_chatwidget_manual(Some("gpt-5.2")).await;
|
||||
chat.thread_id = Some(ThreadId::new());
|
||||
chat.handle_codex_event(Event {
|
||||
id: "turn-start".into(),
|
||||
|
||||
@@ -78,7 +78,7 @@ async fn status_command_renders_immediately_without_rate_limit_refresh() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn status_command_uses_catalog_default_reasoning_when_config_empty() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.1-codex-max")).await;
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.4")).await;
|
||||
chat.config.model_reasoning_effort = None;
|
||||
|
||||
chat.dispatch_command(SlashCommand::Status);
|
||||
@@ -90,7 +90,7 @@ async fn status_command_uses_catalog_default_reasoning_when_config_empty() {
|
||||
other => panic!("expected status output, got {other:?}"),
|
||||
};
|
||||
assert!(
|
||||
rendered.contains("gpt-5.1-codex-max (reasoning medium, summaries auto)"),
|
||||
rendered.contains("gpt-5.4 (reasoning medium, summaries auto)"),
|
||||
"expected /status to render the catalog default reasoning effort, got: {rendered}"
|
||||
);
|
||||
}
|
||||
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
---
|
||||
source: tui/src/app.rs
|
||||
expression: model_migration_copy_to_plain_text(©)
|
||||
---
|
||||
**Codex just got an upgrade. Introducing gpt-5.3-codex.**
|
||||
|
||||
Codex is now powered by gpt-5.3-codex, our most capable agentic coding model yet. It's built for long-running, project-scale work, with mid-turn steering + frequent progress updates so you can collaborate while it runs (and it's faster too).
|
||||
|
||||
Learn more: https://openai.com/index/introducing-gpt-5-3-codex/
|
||||
|
||||
You can keep using gpt-5.1-codex if you prefer.
|
||||
Reference in New Issue
Block a user