mirror of
https://github.com/openai/codex.git
synced 2026-11-09 20:50:33 +00:00
Remove retired model entries while preserving migration prompts (#44250)
## Why Saved model selections can outlive their bundled catalog entries and still need migration guidance. ## What changed - Remove `gpt-5.2` and `gpt-5.4-mini` from the bundled model catalog. - Preserve migration from saved `gpt-5.4-mini` selections to `gpt-5.6-luna` when the old model is absent, while respecting catalog-provided migration metadata. - Update tests and snapshots to use current models or explicit test fixtures, and remove the obsolete prompt fixture. ## Testing Extend migration tests to cover catalog metadata precedence, saved selections, replacement model and reasoning-effort persistence, acknowledged prompts, and missing or hidden migration targets. GitOrigin-RevId: 82beea504cc4a15699677c28a61ec0939af3069c
This commit is contained in:
@@ -1449,17 +1449,17 @@ mod tests {
|
||||
(ReasoningEffortConfig::Ultra, ReasoningEffortConfig::Medium),
|
||||
] {
|
||||
let mut app = make_test_app().await;
|
||||
app.config.model = Some("gpt-5.4".to_string());
|
||||
app.config.model = Some("gpt-5.5".to_string());
|
||||
app.config.model_reasoning_effort = Some(configured_effort.clone());
|
||||
app.chat_widget
|
||||
.set_reasoning_effort(Some(configured_effort));
|
||||
|
||||
let default_effort =
|
||||
app.on_apply_advanced_reasoning("gpt-5.4", ReasoningEffortConfig::Ultra);
|
||||
app.on_apply_advanced_reasoning("gpt-5.5", ReasoningEffortConfig::Ultra);
|
||||
let new_thread_config = app.fresh_session_config();
|
||||
|
||||
assert_eq!(default_effort, Some(expected_default_effort.clone()));
|
||||
assert_eq!(app.chat_widget.current_model(), "gpt-5.4");
|
||||
assert_eq!(app.chat_widget.current_model(), "gpt-5.5");
|
||||
assert_eq!(
|
||||
app.chat_widget.current_reasoning_effort(),
|
||||
Some(ReasoningEffortConfig::Ultra)
|
||||
@@ -1469,7 +1469,7 @@ mod tests {
|
||||
new_thread_config.model.as_deref(),
|
||||
new_thread_config.model_reasoning_effort,
|
||||
),
|
||||
(Some("gpt-5.4"), Some(expected_default_effort))
|
||||
(Some("gpt-5.5"), Some(expected_default_effort))
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1477,15 +1477,15 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn conversation_reasoning_keeps_previous_default_for_ultra_only_model() {
|
||||
let mut app = make_test_app().await;
|
||||
app.config.model = Some("gpt-5.4".to_string());
|
||||
app.config.model = Some("gpt-5.5".to_string());
|
||||
app.config.model_reasoning_effort = Some(ReasoningEffortConfig::Low);
|
||||
let mut preset = app
|
||||
.model_catalog
|
||||
.try_list_models()
|
||||
.expect("model catalog is infallible")
|
||||
.into_iter()
|
||||
.find(|preset| preset.model == "gpt-5.4")
|
||||
.expect("gpt-5.4 preset");
|
||||
.find(|preset| preset.model == "gpt-5.5")
|
||||
.expect("gpt-5.5 preset");
|
||||
preset.model = "ultra-only".to_string();
|
||||
preset.default_reasoning_effort = ReasoningEffortConfig::Ultra;
|
||||
preset.supported_reasoning_efforts = vec![ReasoningEffortPreset {
|
||||
@@ -1509,7 +1509,7 @@ mod tests {
|
||||
new_thread_config.model.as_deref(),
|
||||
new_thread_config.model_reasoning_effort,
|
||||
),
|
||||
(Some("gpt-5.4"), Some(ReasoningEffortConfig::Low))
|
||||
(Some("gpt-5.5"), Some(ReasoningEffortConfig::Low))
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1526,7 +1526,7 @@ mod tests {
|
||||
.handle_key_event(KeyEvent::from(KeyCode::BackTab));
|
||||
|
||||
let default_effort =
|
||||
app.on_apply_advanced_reasoning("gpt-5.4", ReasoningEffortConfig::Ultra);
|
||||
app.on_apply_advanced_reasoning("gpt-5.5", ReasoningEffortConfig::Ultra);
|
||||
|
||||
assert_eq!(default_effort, Some(ReasoningEffortConfig::Low));
|
||||
assert_eq!(
|
||||
|
||||
@@ -110,6 +110,31 @@ pub(super) fn emit_system_bwrap_warning(app_event_tx: &AppEventSender, config: &
|
||||
)));
|
||||
}
|
||||
|
||||
pub(super) fn model_upgrade_for_migration(
|
||||
model: &str,
|
||||
available_models: &[ModelPreset],
|
||||
) -> Option<ModelUpgrade> {
|
||||
if let Some(preset) = available_models.iter().find(|preset| preset.model == model) {
|
||||
return preset.upgrade.clone();
|
||||
}
|
||||
|
||||
// Saved selections can outlive their catalog entries. Keep only their migration metadata.
|
||||
let (target_model, current_name, target_name) = match model {
|
||||
"gpt-5.4-mini" => ("gpt-5.6-luna", "GPT-5.4 Mini", "GPT-5.6 Luna"),
|
||||
_ => return None,
|
||||
};
|
||||
Some(ModelUpgrade {
|
||||
id: target_model.to_string(),
|
||||
migration_config_key: model.to_string(),
|
||||
model_link: None,
|
||||
upgrade_copy: None,
|
||||
migration_markdown: Some(format!(
|
||||
"{current_name} is no longer available\n\nCodex now uses {target_name} in place of {current_name}. Switch to {target_name} to continue.\n"
|
||||
)),
|
||||
retirement_at: None,
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn should_show_model_migration_prompt(
|
||||
current_model: &str,
|
||||
target_model: &str,
|
||||
@@ -133,9 +158,8 @@ pub(super) fn should_show_model_migration_prompt(
|
||||
return false;
|
||||
}
|
||||
|
||||
if available_models
|
||||
.iter()
|
||||
.any(|preset| preset.model == current_model && preset.upgrade.is_some())
|
||||
if model_upgrade_for_migration(current_model, available_models)
|
||||
.is_some_and(|upgrade| upgrade.id == target_model)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -274,10 +298,7 @@ pub(super) async fn handle_model_migration_prompt_if_needed(
|
||||
app_event_tx: &AppEventSender,
|
||||
available_models: &[ModelPreset],
|
||||
) -> std::io::Result<Option<AppExitInfo>> {
|
||||
let upgrade = available_models
|
||||
.iter()
|
||||
.find(|preset| preset.model == model)
|
||||
.and_then(|preset| preset.upgrade.as_ref());
|
||||
let upgrade = model_upgrade_for_migration(model, available_models);
|
||||
|
||||
if let Some(ModelUpgrade {
|
||||
id: target_model,
|
||||
@@ -286,7 +307,7 @@ pub(super) async fn handle_model_migration_prompt_if_needed(
|
||||
upgrade_copy,
|
||||
migration_markdown,
|
||||
..
|
||||
}) = upgrade
|
||||
}) = upgrade.as_ref()
|
||||
{
|
||||
if migration_prompt_hidden(local_settings, migration_config_key.as_str()) {
|
||||
return Ok(None);
|
||||
|
||||
@@ -8796,8 +8796,8 @@ async fn selecting_cyber_model_defaults_active_thread_to_auto_review() {
|
||||
.try_list_models()
|
||||
.expect("model catalog")
|
||||
.into_iter()
|
||||
.find(|model| model.model == "gpt-5.4")
|
||||
.expect("gpt-5.4 model");
|
||||
.find(|model| model.model == "gpt-5.5")
|
||||
.expect("gpt-5.5 model");
|
||||
model.model_specialty = Some(MODEL_SPECIALTY_CYBER.to_string());
|
||||
app.model_catalog = Arc::new(ModelCatalog::new(vec![model]));
|
||||
|
||||
@@ -8834,7 +8834,7 @@ async fn selecting_cyber_model_defaults_active_thread_to_auto_review() {
|
||||
&mut tui,
|
||||
&mut app_server,
|
||||
AppEvent::ApplyAdvancedReasoning {
|
||||
model: "gpt-5.4".to_string(),
|
||||
model: "gpt-5.5".to_string(),
|
||||
effort: ReasoningEffortConfig::High,
|
||||
},
|
||||
)
|
||||
@@ -8845,7 +8845,7 @@ async fn selecting_cyber_model_defaults_active_thread_to_auto_review() {
|
||||
app.handle_event(
|
||||
&mut tui,
|
||||
&mut app_server,
|
||||
AppEvent::UpdateModel("gpt-5.4".to_string()),
|
||||
AppEvent::UpdateModel("gpt-5.5".to_string()),
|
||||
)
|
||||
.await
|
||||
.expect("model selection should succeed");
|
||||
@@ -8993,8 +8993,8 @@ async fn selecting_cyber_model_falls_back_to_user_when_auto_review_is_unavailabl
|
||||
.try_list_models()
|
||||
.expect("model catalog")
|
||||
.into_iter()
|
||||
.find(|model| model.model == "gpt-5.4")
|
||||
.expect("gpt-5.4 model");
|
||||
.find(|model| model.model == "gpt-5.5")
|
||||
.expect("gpt-5.5 model");
|
||||
model.model_specialty = Some(MODEL_SPECIALTY_CYBER.to_string());
|
||||
app.model_catalog = Arc::new(ModelCatalog::new(vec![model]));
|
||||
let _ = app.config.features.disable(Feature::GuardianApproval);
|
||||
@@ -9003,7 +9003,7 @@ async fn selecting_cyber_model_falls_back_to_user_when_auto_review_is_unavailabl
|
||||
app.active_thread_id = Some(ThreadId::new());
|
||||
|
||||
let params = app
|
||||
.active_thread_model_setting_update_params("gpt-5.4".to_string())
|
||||
.active_thread_model_setting_update_params("gpt-5.5".to_string())
|
||||
.expect("active thread should produce update params");
|
||||
|
||||
assert_eq!(
|
||||
@@ -9048,8 +9048,8 @@ async fn selecting_cyber_model_respects_auto_review_requirements() {
|
||||
.try_list_models()
|
||||
.expect("model catalog")
|
||||
.into_iter()
|
||||
.find(|model| model.model == "gpt-5.4")
|
||||
.expect("gpt-5.4 model");
|
||||
.find(|model| model.model == "gpt-5.5")
|
||||
.expect("gpt-5.5 model");
|
||||
model.model_specialty = Some(MODEL_SPECIALTY_CYBER.to_string());
|
||||
app.model_catalog = Arc::new(ModelCatalog::new(vec![model]));
|
||||
|
||||
@@ -9074,7 +9074,7 @@ async fn selecting_cyber_model_respects_auto_review_requirements() {
|
||||
app.handle_event(
|
||||
&mut tui,
|
||||
&mut app_server,
|
||||
AppEvent::UpdateModel("gpt-5.4".to_string()),
|
||||
AppEvent::UpdateModel("gpt-5.5".to_string()),
|
||||
)
|
||||
.await
|
||||
.expect("model selection should succeed");
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use super::*;
|
||||
use crate::chatwidget::UserMessage;
|
||||
use crate::chatwidget::tests::helpers::normalize_snapshot_paths;
|
||||
use crate::chatwidget::tests::helpers::set_fast_mode_test_catalog_for_models;
|
||||
use codex_app_server_protocol::GetAccountRateLimitsResponse;
|
||||
use codex_app_server_protocol::ThreadSettingsUpdateParams;
|
||||
use pretty_assertions::assert_eq;
|
||||
@@ -10,9 +11,9 @@ pub(super) fn fallback_response() -> GetAccountRateLimitsResponse {
|
||||
serde_json::from_value(json!({
|
||||
"accountId": "workspace-a", "rateLimits": {},
|
||||
"rateLimitUpsell": {
|
||||
"banner_type": "model_recovery", "model_slug": "gpt-5.4",
|
||||
"blocked_model_slug": "gpt-5.4",
|
||||
"fallback_model_slugs": ["unavailable-model", "gpt-5.4", "gpt-5.2"],
|
||||
"banner_type": "model_recovery", "model_slug": "gpt-5.5",
|
||||
"blocked_model_slug": "gpt-5.5",
|
||||
"fallback_model_slugs": ["unavailable-model", "gpt-5.5", "gpt-5.6-terra"],
|
||||
"title": "Selected model usage exhausted",
|
||||
"description": "Add credits to use this model again.",
|
||||
"presentation": "dismissible",
|
||||
@@ -24,9 +25,9 @@ pub(super) fn fallback_response() -> GetAccountRateLimitsResponse {
|
||||
|
||||
fn configure_fallback_model(app: &mut App) {
|
||||
set_chatgpt_auth(&mut app.chat_widget);
|
||||
set_fast_mode_test_catalog(&mut app.chat_widget);
|
||||
set_fast_mode_test_catalog_for_models(&mut app.chat_widget, "gpt-5.5", "gpt-5.6-terra");
|
||||
app.model_catalog = app.chat_widget.model_catalog();
|
||||
app.chat_widget.set_model("gpt-5.4");
|
||||
app.chat_widget.set_model("gpt-5.5");
|
||||
}
|
||||
|
||||
pub(super) async fn start_fallback_thread(
|
||||
@@ -65,7 +66,7 @@ async fn backend_banner_fallback_updates_task_settings_and_keeps_notice() -> Res
|
||||
.set_service_tier(Some(ServiceTier::Fast.request_value().to_string()));
|
||||
let mut expected_mode = app.chat_widget.effective_collaboration_mode();
|
||||
assert_eq!(expected_mode.mode, mode_kind);
|
||||
expected_mode.settings.model = "gpt-5.2".into();
|
||||
expected_mode.settings.model = "gpt-5.6-terra".into();
|
||||
expected_mode.settings.reasoning_effort = Some(ReasoningEffortConfig::Medium);
|
||||
let default_model = app.config.model.clone();
|
||||
let default_plan_effort = app.config.plan_mode_reasoning_effort.clone();
|
||||
@@ -99,7 +100,7 @@ async fn backend_banner_fallback_updates_task_settings_and_keeps_notice() -> Res
|
||||
serde_json::from_value::<ThreadSettingsUpdateParams>(sent[0].params.clone().unwrap())?,
|
||||
ThreadSettingsUpdateParams {
|
||||
thread_id: thread_id.to_string(),
|
||||
model: Some("gpt-5.2".into()),
|
||||
model: Some("gpt-5.6-terra".into()),
|
||||
effort: Some(ReasoningEffortConfig::Medium),
|
||||
collaboration_mode: Some(expected_mode.clone()),
|
||||
service_tier: Some(Some("default".into())),
|
||||
@@ -146,7 +147,7 @@ async fn backend_banner_fallback_updates_task_settings_and_keeps_notice() -> Res
|
||||
healthy.rate_limit_upsell = None;
|
||||
app.chat_widget.update_backend_banner(&healthy);
|
||||
app.apply_backend_banner_fallback(&mut server).await;
|
||||
assert_eq!(app.chat_widget.current_model(), "gpt-5.2");
|
||||
assert_eq!(app.chat_widget.current_model(), "gpt-5.6-terra");
|
||||
assert_eq!(requests.lock().unwrap().len(), 1);
|
||||
assert!(
|
||||
!std::iter::from_fn(|| events.try_recv().ok())
|
||||
@@ -166,7 +167,7 @@ async fn backend_banner_fallback_updates_task_settings_and_keeps_notice() -> Res
|
||||
app.chat_widget
|
||||
.handle_key_event(KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE));
|
||||
assert_matches!(next_user_turn_op(&mut ops), AppCommand::UserTurn { model, collaboration_mode: Some(mode), .. }
|
||||
if model == "gpt-5.2" && mode == expected_mode);
|
||||
if model == "gpt-5.6-terra" && mode == expected_mode);
|
||||
server.shutdown().await?;
|
||||
proxy.await??;
|
||||
}
|
||||
@@ -207,7 +208,7 @@ async fn backend_banner_fallback_uses_current_task_and_accepted_generation() ->
|
||||
)
|
||||
.await?;
|
||||
if generation == read_generation {
|
||||
assert_eq!(app.chat_widget.current_model(), "gpt-5.4");
|
||||
assert_eq!(app.chat_widget.current_model(), "gpt-5.5");
|
||||
assert!(requests.lock().unwrap().is_empty());
|
||||
}
|
||||
}
|
||||
@@ -217,7 +218,7 @@ async fn backend_banner_fallback_uses_current_task_and_accepted_generation() ->
|
||||
sent[0].params.as_ref().unwrap()["threadId"],
|
||||
active_id.to_string()
|
||||
);
|
||||
assert_eq!(app.chat_widget.current_model(), "gpt-5.2");
|
||||
assert_eq!(app.chat_widget.current_model(), "gpt-5.6-terra");
|
||||
server.shutdown().await?;
|
||||
proxy.await??;
|
||||
Ok(())
|
||||
@@ -231,13 +232,13 @@ async fn backend_banner_fallback_keeps_existing_recovery_without_candidate() ->
|
||||
for (replacement, expect_legacy_banner) in [
|
||||
(json!([]), true),
|
||||
(json!(["unavailable-model"]), false),
|
||||
(json!(["gpt-5.4"]), false),
|
||||
(json!(["gpt-5.5"]), false),
|
||||
] {
|
||||
let mut response = fallback_response();
|
||||
response.rate_limit_upsell.as_mut().unwrap()["fallback_model_slugs"] = replacement;
|
||||
app.chat_widget.update_backend_banner(&response);
|
||||
app.apply_backend_banner_fallback(&mut server).await;
|
||||
assert_eq!(app.chat_widget.current_model(), "gpt-5.4");
|
||||
assert_eq!(app.chat_widget.current_model(), "gpt-5.5");
|
||||
let rendered = render_bottom_popup(&app.chat_widget, /*width*/ 72);
|
||||
assert_eq!(rendered.contains("View usage"), expect_legacy_banner);
|
||||
if !expect_legacy_banner {
|
||||
@@ -247,10 +248,10 @@ async fn backend_banner_fallback_keeps_existing_recovery_without_candidate() ->
|
||||
);
|
||||
}
|
||||
}
|
||||
app.chat_widget.set_model("gpt-5.2");
|
||||
app.chat_widget.set_model("gpt-5.6-terra");
|
||||
app.chat_widget.update_backend_banner(&fallback_response());
|
||||
app.apply_backend_banner_fallback(&mut server).await;
|
||||
assert_eq!(app.chat_widget.current_model(), "gpt-5.2");
|
||||
assert_eq!(app.chat_widget.current_model(), "gpt-5.6-terra");
|
||||
assert!(
|
||||
render_bottom_popup(&app.chat_widget, /*width*/ 72)
|
||||
.contains("Selected model usage exhausted")
|
||||
@@ -310,7 +311,7 @@ async fn backend_banner_fallback_handles_settings_failure_and_queued_manual_sele
|
||||
} else {
|
||||
if settings_requests == 1 {
|
||||
// A manual selection arriving while the RPC is pending must win afterward.
|
||||
manual_selection_tx.send(AppEvent::UpdateModel("gpt-5.4".into()));
|
||||
manual_selection_tx.send(AppEvent::UpdateModel("gpt-5.5".into()));
|
||||
}
|
||||
json!({"id": request["id"], "result": {}})
|
||||
}
|
||||
@@ -369,7 +370,7 @@ async fn backend_banner_fallback_handles_settings_failure_and_queued_manual_sele
|
||||
if use_reserve {
|
||||
"gpt-reserve"
|
||||
} else {
|
||||
"gpt-5.2"
|
||||
"gpt-5.6-terra"
|
||||
}
|
||||
);
|
||||
let manual_selection = queued
|
||||
@@ -380,7 +381,7 @@ async fn backend_banner_fallback_handles_settings_failure_and_queued_manual_sele
|
||||
app.handle_event(&mut tui, &mut server, manual_selection)
|
||||
.await?;
|
||||
}
|
||||
assert_eq!(app.chat_widget.current_model(), "gpt-5.4");
|
||||
assert_eq!(app.chat_widget.current_model(), "gpt-5.5");
|
||||
assert!(!render_bottom_popup(&app.chat_widget, /*width*/ 72).contains("View usage"));
|
||||
if use_reserve && error_code.is_some() {
|
||||
let rendered = render_bottom_popup(&app.chat_widget, /*width*/ 90);
|
||||
@@ -447,7 +448,7 @@ async fn backend_banner_fallback_preserves_permissions_for_first_eligible_cyber_
|
||||
let mut models = app.model_catalog.try_list_models()?;
|
||||
let mut cyber_model = models
|
||||
.iter()
|
||||
.find(|model| model.model == "gpt-5.2")
|
||||
.find(|model| model.model == "gpt-5.6-terra")
|
||||
.unwrap()
|
||||
.clone();
|
||||
// Like the manual-selection tests, classify a fixture model through catalog metadata.
|
||||
@@ -465,12 +466,16 @@ async fn backend_banner_fallback_preserves_permissions_for_first_eligible_cyber_
|
||||
app.replace_chat_widget(ChatWidget::new_with_app_event(init));
|
||||
app.chat_widget
|
||||
.handle_thread_session(app.primary_session_configured.clone().unwrap());
|
||||
app.chat_widget.set_model("gpt-5.4");
|
||||
app.chat_widget.set_model("gpt-5.5");
|
||||
app.chat_widget
|
||||
.set_reasoning_effort(Some(ReasoningEffortConfig::Medium));
|
||||
let mut response = fallback_response();
|
||||
response.rate_limit_upsell.as_mut().unwrap()["fallback_model_slugs"] =
|
||||
json!(["unavailable-model", "gpt-5.4", cyber_model.model, "gpt-5.2"]);
|
||||
response.rate_limit_upsell.as_mut().unwrap()["fallback_model_slugs"] = json!([
|
||||
"unavailable-model",
|
||||
"gpt-5.5",
|
||||
cyber_model.model,
|
||||
"gpt-5.6-terra"
|
||||
]);
|
||||
app.chat_widget.update_backend_banner(&response);
|
||||
app.apply_backend_banner_fallback(&mut server).await;
|
||||
assert_eq!(app.chat_widget.current_model(), cyber_model.model);
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
use super::backend_banner_fallback_tests::fallback_response;
|
||||
use super::*;
|
||||
use crate::chatwidget::UserMessage;
|
||||
use crate::chatwidget::tests::helpers::set_fast_mode_test_catalog_for_models;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
#[tokio::test]
|
||||
async fn backend_banner_fallback_applies_before_initial_and_queued_startup_prompts() -> Result<()> {
|
||||
let (mut app, mut events, _ops) = make_test_app_with_channels().await;
|
||||
let mut config = app.config.clone();
|
||||
config.model = Some("gpt-5.4".into());
|
||||
config.model = Some("gpt-5.5".into());
|
||||
let mut tui = crate::tui::test_support::make_test_tui()?;
|
||||
let init = app.chatwidget_init_for_forked_or_resumed_thread(
|
||||
&mut tui,
|
||||
@@ -16,10 +17,10 @@ async fn backend_banner_fallback_applies_before_initial_and_queued_startup_promp
|
||||
);
|
||||
app.replace_chat_widget(ChatWidget::new_with_app_event(init));
|
||||
set_chatgpt_auth(&mut app.chat_widget);
|
||||
set_fast_mode_test_catalog(&mut app.chat_widget);
|
||||
set_fast_mode_test_catalog_for_models(&mut app.chat_widget, "gpt-5.5", "gpt-5.6-terra");
|
||||
app.model_catalog = app.chat_widget.model_catalog();
|
||||
app.pending_startup_thread_start = true;
|
||||
app.chat_widget.set_model("gpt-5.4");
|
||||
app.chat_widget.set_model("gpt-5.5");
|
||||
app.chat_widget
|
||||
.set_queue_submissions_until_session_configured(/*queue*/ true);
|
||||
app.chat_widget
|
||||
@@ -68,7 +69,7 @@ async fn backend_banner_fallback_applies_before_initial_and_queued_startup_promp
|
||||
assert_eq!(
|
||||
submissions,
|
||||
vec![(
|
||||
"gpt-5.2".into(),
|
||||
"gpt-5.6-terra".into(),
|
||||
vec![UserInput::Text {
|
||||
text: "initial prompt".into(),
|
||||
text_elements: Vec::new(),
|
||||
|
||||
@@ -29,7 +29,7 @@ pub(super) fn configure_reserve_catalog(app: &mut App) {
|
||||
let mut models = app.model_catalog.try_list_models().unwrap();
|
||||
let mut reserve = models
|
||||
.iter()
|
||||
.find(|model| model.model == "gpt-5.4")
|
||||
.find(|model| model.model == "gpt-5.5")
|
||||
.unwrap()
|
||||
.clone();
|
||||
reserve.model = "gpt-reserve".into();
|
||||
@@ -37,7 +37,7 @@ pub(super) fn configure_reserve_catalog(app: &mut App) {
|
||||
models.push(reserve);
|
||||
let original = models
|
||||
.iter_mut()
|
||||
.find(|model| model.model == "gpt-5.4")
|
||||
.find(|model| model.model == "gpt-5.5")
|
||||
.unwrap();
|
||||
let mut high = original.supported_reasoning_efforts[0].clone();
|
||||
high.effort = ReasoningEffortConfig::High;
|
||||
@@ -54,7 +54,7 @@ pub(super) fn configure_reserve_catalog(app: &mut App) {
|
||||
app.replace_chat_widget(ChatWidget::new_with_app_event(init));
|
||||
app.chat_widget
|
||||
.handle_thread_session(app.primary_session_configured.clone().unwrap());
|
||||
app.chat_widget.set_model("gpt-5.4");
|
||||
app.chat_widget.set_model("gpt-5.5");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -87,7 +87,7 @@ async fn luna_reserve_recovery_survives_task_reconstruction() -> Result<()> {
|
||||
recovered.rate_limit_upsell = None;
|
||||
app.chat_widget.update_backend_banner(&recovered);
|
||||
app.apply_backend_banner_fallback(&mut server).await;
|
||||
assert_eq!(app.chat_widget.current_model(), "gpt-5.4");
|
||||
assert_eq!(app.chat_widget.current_model(), "gpt-5.5");
|
||||
assert_eq!(
|
||||
app.chat_widget.current_reasoning_effort(),
|
||||
Some(ReasoningEffortConfig::High)
|
||||
@@ -236,8 +236,8 @@ async fn luna_reserve_recovery_restores_task_and_pending_turn_after_fresh_backen
|
||||
// A rounded percentage is not an authorization decision; backend allowed=true wins.
|
||||
for (request_id, hard_stop_generation, expected) in [
|
||||
(2, generation.wrapping_sub(1), "gpt-reserve"),
|
||||
(3, generation, "gpt-5.4"),
|
||||
(4, generation, "gpt-5.4"),
|
||||
(3, generation, "gpt-5.5"),
|
||||
(4, generation, "gpt-5.5"),
|
||||
] {
|
||||
app.handle_event(
|
||||
&mut tui,
|
||||
@@ -260,7 +260,7 @@ async fn luna_reserve_recovery_restores_task_and_pending_turn_after_fresh_backen
|
||||
app.chat_widget
|
||||
.apply_reserve_fallback_to_pending_turn(&mut pending);
|
||||
assert_matches!(pending, AppCommand::UserTurn { model, effort, collaboration_mode, .. }
|
||||
if model == "gpt-5.4" && effort == original_mode.reasoning_effort()
|
||||
if model == "gpt-5.5" && effort == original_mode.reasoning_effort()
|
||||
&& collaboration_mode == Some(original_mode.clone()));
|
||||
let sent = requests.lock().unwrap().clone();
|
||||
assert_eq!(sent.len(), 2);
|
||||
@@ -270,7 +270,7 @@ async fn luna_reserve_recovery_restores_task_and_pending_turn_after_fresh_backen
|
||||
restored,
|
||||
ThreadSettingsUpdateParams {
|
||||
thread_id: app.active_thread_id.unwrap().to_string(),
|
||||
model: Some("gpt-5.4".into()),
|
||||
model: Some("gpt-5.5".into()),
|
||||
effort: Some(ReasoningEffortConfig::High),
|
||||
collaboration_mode: Some(original_mode),
|
||||
service_tier: None,
|
||||
@@ -341,7 +341,7 @@ async fn luna_reserve_recovery_requires_permission_and_no_remaining_blocker() ->
|
||||
app.chat_widget.clear_backend_banner();
|
||||
app.chat_widget.update_backend_banner(&recovered);
|
||||
app.apply_backend_banner_fallback(&mut server).await;
|
||||
assert_eq!(app.chat_widget.current_model(), "gpt-5.4");
|
||||
assert_eq!(app.chat_widget.current_model(), "gpt-5.5");
|
||||
assert_eq!(requests.lock().unwrap().len(), 1);
|
||||
server.shutdown().await?;
|
||||
proxy.await??;
|
||||
@@ -365,8 +365,8 @@ async fn luna_reserve_recovery_does_not_override_manual_choice_or_account_change
|
||||
recovered.account_id = Some("workspace-b".into());
|
||||
"gpt-reserve"
|
||||
} else {
|
||||
app.chat_widget.set_model("gpt-5.2");
|
||||
"gpt-5.2"
|
||||
app.chat_widget.set_model("gpt-5.6-terra");
|
||||
"gpt-5.6-terra"
|
||||
};
|
||||
app.chat_widget.update_backend_banner(&recovered);
|
||||
app.apply_backend_banner_fallback(&mut server).await;
|
||||
|
||||
@@ -18,7 +18,7 @@ async fn model_picker_refresh_updates_app_catalog_from_app_server() -> Result<()
|
||||
let fast = Some(ServiceTier::Fast.request_value());
|
||||
let thread_id = ThreadId::new();
|
||||
let mut session = test_thread_session(thread_id, test_path_buf("/tmp/project"));
|
||||
session.model = "gpt-5.4".to_string();
|
||||
session.model = "gpt-5.5".to_string();
|
||||
app.primary_thread_id = Some(thread_id);
|
||||
app.active_thread_id = Some(thread_id);
|
||||
app.primary_session_configured = Some(session.clone());
|
||||
@@ -72,7 +72,7 @@ async fn model_picker_refresh_updates_app_catalog_from_app_server() -> Result<()
|
||||
};
|
||||
assert!(!models.is_empty());
|
||||
for model in models.iter_mut() {
|
||||
model.is_default = model.model == "gpt-5.4";
|
||||
model.is_default = model.model == "gpt-5.5";
|
||||
model.default_service_tier = model
|
||||
.is_default
|
||||
.then(|| ServiceTier::Fast.request_value().to_string());
|
||||
@@ -111,7 +111,7 @@ async fn model_picker_refresh_updates_app_catalog_from_app_server() -> Result<()
|
||||
let mut config = app.config.clone();
|
||||
config.service_tier = None;
|
||||
config.features.enable(Feature::FastMode)?;
|
||||
for (model, expected_tier) in [(None, fast), (Some("gpt-5.2"), Some("default"))] {
|
||||
for (model, expected_tier) in [(None, fast), (Some("gpt-5.6-terra"), Some("default"))] {
|
||||
config.model = model.map(str::to_string);
|
||||
let started = app_server.start_thread(&config).await?;
|
||||
assert_eq!(started.session.service_tier.as_deref(), expected_tier);
|
||||
@@ -122,20 +122,23 @@ async fn model_picker_refresh_updates_app_catalog_from_app_server() -> Result<()
|
||||
|
||||
fn model_presets_with_test_upgrades() -> Vec<ModelPreset> {
|
||||
let mut presets = all_model_presets();
|
||||
for model in ["gpt-5.2", "gpt-5.4"] {
|
||||
let preset = presets
|
||||
.iter_mut()
|
||||
.find(|preset| preset.model == model)
|
||||
.unwrap_or_else(|| panic!("{model} preset present"));
|
||||
preset.upgrade = Some(ModelUpgrade {
|
||||
id: "gpt-5.5".to_string(),
|
||||
migration_config_key: "hide_test_migration_prompt".to_string(),
|
||||
model_link: None,
|
||||
upgrade_copy: None,
|
||||
migration_markdown: None,
|
||||
retirement_at: None,
|
||||
});
|
||||
}
|
||||
let mut preset = presets
|
||||
.iter()
|
||||
.find(|preset| preset.model == "gpt-5.5")
|
||||
.expect("current model template")
|
||||
.clone();
|
||||
// Catalog-provided migration metadata is independent of the retired model's prompts.
|
||||
preset.id = "gpt-5.2".to_string();
|
||||
preset.model = "gpt-5.2".to_string();
|
||||
preset.upgrade = Some(ModelUpgrade {
|
||||
id: "gpt-5.5".to_string(),
|
||||
migration_config_key: "hide_test_migration_prompt".to_string(),
|
||||
model_link: None,
|
||||
upgrade_copy: None,
|
||||
migration_markdown: None,
|
||||
retirement_at: None,
|
||||
});
|
||||
presets.push(preset);
|
||||
presets
|
||||
}
|
||||
|
||||
@@ -175,13 +178,39 @@ async fn model_migration_prompt_only_shows_for_deprecated_models() {
|
||||
"gpt-5.2", "gpt-5.5", &seen, &presets
|
||||
));
|
||||
assert!(should_show_model_migration_prompt(
|
||||
"gpt-5.4", "gpt-5.5", &seen, &presets
|
||||
"gpt-5.4",
|
||||
"gpt-5.6-terra",
|
||||
&seen,
|
||||
&presets
|
||||
));
|
||||
assert!(!should_show_model_migration_prompt(
|
||||
"gpt-5.4", "gpt-5.4", &seen, &presets
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn retired_model_migration_respects_catalog_metadata() {
|
||||
let mut presets = model_presets_with_test_upgrades();
|
||||
let current = presets
|
||||
.iter_mut()
|
||||
.find(|preset| preset.model == "gpt-5.2")
|
||||
.expect("test preset");
|
||||
current.id = "gpt-5.4-mini".to_string();
|
||||
current.model = "gpt-5.4-mini".to_string();
|
||||
let expected = current.upgrade.clone();
|
||||
assert_eq!(
|
||||
model_upgrade_for_migration("gpt-5.4-mini", &presets),
|
||||
expected
|
||||
);
|
||||
|
||||
presets
|
||||
.iter_mut()
|
||||
.find(|preset| preset.model == "gpt-5.4-mini")
|
||||
.expect("catalog preset")
|
||||
.upgrade = None;
|
||||
assert_eq!(model_upgrade_for_migration("gpt-5.4-mini", &presets), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn select_model_availability_nux_picks_only_eligible_model() {
|
||||
let mut presets = all_model_presets();
|
||||
@@ -190,10 +219,10 @@ fn select_model_availability_nux_picks_only_eligible_model() {
|
||||
});
|
||||
let target = presets
|
||||
.iter_mut()
|
||||
.find(|preset| preset.model == "gpt-5.4")
|
||||
.find(|preset| preset.model == "gpt-5.6-terra")
|
||||
.expect("target preset present");
|
||||
target.availability_nux = Some(ModelAvailabilityNux {
|
||||
message: "gpt-5.4 is available".to_string(),
|
||||
message: "gpt-5.6-terra is available".to_string(),
|
||||
});
|
||||
|
||||
let selected = select_model_availability_nux(&presets, &model_availability_nux_config(&[]));
|
||||
@@ -201,8 +230,8 @@ fn select_model_availability_nux_picks_only_eligible_model() {
|
||||
assert_eq!(
|
||||
selected,
|
||||
Some(StartupTooltipOverride {
|
||||
model_slug: "gpt-5.4".to_string(),
|
||||
message: "gpt-5.4 is available".to_string(),
|
||||
model_slug: "gpt-5.6-terra".to_string(),
|
||||
message: "gpt-5.6-terra is available".to_string(),
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -215,22 +244,22 @@ fn select_model_availability_nux_does_not_fall_back_to_older_announcement() {
|
||||
});
|
||||
let gpt_5 = presets
|
||||
.iter_mut()
|
||||
.find(|preset| preset.model == "gpt-5.4")
|
||||
.expect("gpt-5.4 preset present");
|
||||
.find(|preset| preset.model == "gpt-5.6-terra")
|
||||
.expect("gpt-5.6-terra preset present");
|
||||
gpt_5.availability_nux = Some(ModelAvailabilityNux {
|
||||
message: "gpt-5.4 is available".to_string(),
|
||||
message: "gpt-5.6-terra 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");
|
||||
.find(|preset| preset.model == "gpt-5.5")
|
||||
.expect("gpt-5.5 preset present");
|
||||
gpt_5_2.availability_nux = Some(ModelAvailabilityNux {
|
||||
message: "gpt-5.4-mini is available".to_string(),
|
||||
message: "gpt-5.5 is available".to_string(),
|
||||
});
|
||||
|
||||
let selected = select_model_availability_nux(
|
||||
&presets,
|
||||
&model_availability_nux_config(&[("gpt-5.4", MODEL_AVAILABILITY_NUX_MAX_SHOW_COUNT)]),
|
||||
&model_availability_nux_config(&[("gpt-5.6-terra", MODEL_AVAILABILITY_NUX_MAX_SHOW_COUNT)]),
|
||||
);
|
||||
|
||||
assert_eq!(selected, None);
|
||||
@@ -244,15 +273,15 @@ fn select_model_availability_nux_uses_existing_model_order_as_priority() {
|
||||
});
|
||||
let first = presets
|
||||
.iter_mut()
|
||||
.find(|preset| preset.model == "gpt-5.4-mini")
|
||||
.expect("gpt-5.4-mini preset present");
|
||||
.find(|preset| preset.model == "gpt-5.5")
|
||||
.expect("gpt-5.5 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");
|
||||
.find(|preset| preset.model == "gpt-5.6-terra")
|
||||
.expect("gpt-5.6-terra preset present");
|
||||
second.availability_nux = Some(ModelAvailabilityNux {
|
||||
message: "second".to_string(),
|
||||
});
|
||||
@@ -262,7 +291,7 @@ fn select_model_availability_nux_uses_existing_model_order_as_priority() {
|
||||
assert_eq!(
|
||||
selected,
|
||||
Some(StartupTooltipOverride {
|
||||
model_slug: "gpt-5.4".to_string(),
|
||||
model_slug: "gpt-5.6-terra".to_string(),
|
||||
message: "second".to_string(),
|
||||
})
|
||||
);
|
||||
@@ -276,15 +305,15 @@ fn select_model_availability_nux_returns_none_when_all_models_are_exhausted() {
|
||||
});
|
||||
let target = presets
|
||||
.iter_mut()
|
||||
.find(|preset| preset.model == "gpt-5.4")
|
||||
.find(|preset| preset.model == "gpt-5.6-terra")
|
||||
.expect("target preset present");
|
||||
target.availability_nux = Some(ModelAvailabilityNux {
|
||||
message: "gpt-5.4 is available".to_string(),
|
||||
message: "gpt-5.6-terra is available".to_string(),
|
||||
});
|
||||
|
||||
let selected = select_model_availability_nux(
|
||||
&presets,
|
||||
&model_availability_nux_config(&[("gpt-5.4", MODEL_AVAILABILITY_NUX_MAX_SHOW_COUNT)]),
|
||||
&model_availability_nux_config(&[("gpt-5.6-terra", MODEL_AVAILABILITY_NUX_MAX_SHOW_COUNT)]),
|
||||
);
|
||||
|
||||
assert_eq!(selected, None);
|
||||
@@ -304,10 +333,10 @@ async fn prepare_startup_tooltip_override_persists_model_availability_nux_count(
|
||||
});
|
||||
let target = presets
|
||||
.iter_mut()
|
||||
.find(|preset| preset.model == "gpt-5.4")
|
||||
.find(|preset| preset.model == "gpt-5.6-terra")
|
||||
.expect("target preset present");
|
||||
target.availability_nux = Some(ModelAvailabilityNux {
|
||||
message: "gpt-5.4 is available".to_string(),
|
||||
message: "gpt-5.6-terra is available".to_string(),
|
||||
});
|
||||
|
||||
let mut local_settings = crate::local_settings::LocalSettings::from(&config);
|
||||
@@ -318,10 +347,10 @@ async fn prepare_startup_tooltip_override_persists_model_availability_nux_count(
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_eq!(tooltip.as_deref(), Some("gpt-5.4 is available"));
|
||||
assert_eq!(tooltip.as_deref(), Some("gpt-5.6-terra is available"));
|
||||
assert_eq!(
|
||||
local_settings.tui.model_availability_nux.shown_count,
|
||||
HashMap::from([("gpt-5.4".to_string(), 1)])
|
||||
HashMap::from([("gpt-5.6-terra".to_string(), 1)])
|
||||
);
|
||||
|
||||
let reloaded = ConfigBuilder::default()
|
||||
@@ -331,125 +360,151 @@ async fn prepare_startup_tooltip_override_persists_model_availability_nux_count(
|
||||
.expect("reloaded config");
|
||||
assert_eq!(
|
||||
reloaded.model_availability_nux.shown_count,
|
||||
HashMap::from([("gpt-5.4".to_string(), 1)])
|
||||
HashMap::from([("gpt-5.6-terra".to_string(), 1)])
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn accepted_model_migration_persists_target_default_reasoning_effort() {
|
||||
let codex_home = tempdir().expect("temp codex home");
|
||||
let mut config = ConfigBuilder::default()
|
||||
.codex_home(codex_home.path().to_path_buf())
|
||||
.build()
|
||||
.await
|
||||
.expect("config");
|
||||
config.model = Some("gpt-5.2".to_string());
|
||||
config.model_reasoning_effort = Some(ReasoningEffortConfig::XHigh);
|
||||
async fn accepted_model_migration_persists_target_default_reasoning_effort() -> Result<()> {
|
||||
let presets = all_model_presets();
|
||||
let mut migration_copies = Vec::new();
|
||||
for (model, replacement) in [
|
||||
("gpt-5.4", "gpt-5.6-terra"),
|
||||
("gpt-5.4-mini", "gpt-5.6-luna"),
|
||||
] {
|
||||
let codex_home = tempdir()?;
|
||||
std::fs::write(
|
||||
codex_home.path().join("config.toml"),
|
||||
format!("model = \"{model}\"\nmodel_reasoning_effort = \"xhigh\"\n"),
|
||||
)?;
|
||||
let mut config = ConfigBuilder::default()
|
||||
.codex_home(codex_home.path().to_path_buf())
|
||||
.build()
|
||||
.await?;
|
||||
let current_model = config.model.clone().expect("saved model");
|
||||
let upgrade = model_upgrade_for_migration(¤t_model, &presets)
|
||||
.expect("retired selection should still migrate");
|
||||
assert_eq!(upgrade.id, replacement);
|
||||
assert!(should_show_model_migration_prompt(
|
||||
¤t_model,
|
||||
&upgrade.id,
|
||||
&BTreeMap::new(),
|
||||
&presets,
|
||||
));
|
||||
let target =
|
||||
target_preset_for_upgrade(&presets, &upgrade.id).expect("available replacement");
|
||||
let target_effort = target.default_reasoning_effort.clone();
|
||||
let can_opt_out = presets.iter().any(|preset| preset.model == current_model);
|
||||
let copy = migration_copy_for_models(
|
||||
¤t_model,
|
||||
&upgrade.id,
|
||||
upgrade.model_link,
|
||||
upgrade.upgrade_copy,
|
||||
upgrade.migration_markdown,
|
||||
target.display_name.clone(),
|
||||
Some(target.description.clone()),
|
||||
can_opt_out,
|
||||
);
|
||||
migration_copies.push(model_migration_copy_to_plain_text(©));
|
||||
|
||||
let (tx_raw, mut rx) = unbounded_channel();
|
||||
let app_event_tx = AppEventSender::new(tx_raw);
|
||||
let (tx_raw, mut rx) = unbounded_channel();
|
||||
let app_event_tx = AppEventSender::new(tx_raw);
|
||||
apply_accepted_model_migration(
|
||||
&mut config,
|
||||
&app_event_tx,
|
||||
current_model,
|
||||
upgrade.id,
|
||||
target_effort.clone(),
|
||||
);
|
||||
|
||||
apply_accepted_model_migration(
|
||||
&mut config,
|
||||
&app_event_tx,
|
||||
"gpt-5.2".to_string(),
|
||||
"gpt-5.4".to_string(),
|
||||
ReasoningEffortConfig::Medium,
|
||||
);
|
||||
assert_eq!(config.model.as_deref(), Some(replacement));
|
||||
assert_eq!(config.model_reasoning_effort, Some(target_effort.clone()));
|
||||
|
||||
assert_eq!(config.model.as_deref(), Some("gpt-5.4"));
|
||||
assert_eq!(
|
||||
config.model_reasoning_effort,
|
||||
Some(ReasoningEffortConfig::Medium)
|
||||
);
|
||||
let acknowledged = rx.try_recv().expect("acknowledged event");
|
||||
assert_matches!(
|
||||
acknowledged,
|
||||
AppEvent::PersistModelMigrationPromptAcknowledged { from_model, to_model }
|
||||
if from_model == model && to_model == replacement
|
||||
);
|
||||
|
||||
let acknowledged = rx.try_recv().expect("acknowledged event");
|
||||
assert_matches!(
|
||||
acknowledged,
|
||||
AppEvent::PersistModelMigrationPromptAcknowledged { from_model, to_model }
|
||||
if from_model == "gpt-5.2" && to_model == "gpt-5.4"
|
||||
);
|
||||
let update_model = rx.try_recv().expect("update model event");
|
||||
assert_matches!(
|
||||
update_model,
|
||||
AppEvent::UpdateModel(updated_model) if updated_model == replacement
|
||||
);
|
||||
|
||||
let update_model = rx.try_recv().expect("update model event");
|
||||
assert_matches!(
|
||||
update_model,
|
||||
AppEvent::UpdateModel(model) if model == "gpt-5.4"
|
||||
);
|
||||
let update_effort = rx.try_recv().expect("update effort event");
|
||||
assert_matches!(
|
||||
update_effort,
|
||||
AppEvent::UpdateReasoningEffort(Some(effort)) if effort == target_effort
|
||||
);
|
||||
|
||||
let update_effort = rx.try_recv().expect("update effort event");
|
||||
assert_matches!(
|
||||
update_effort,
|
||||
AppEvent::UpdateReasoningEffort(Some(ReasoningEffortConfig::Medium))
|
||||
);
|
||||
let persist_selection = rx.try_recv().expect("persist model selection event");
|
||||
assert_matches!(
|
||||
persist_selection,
|
||||
AppEvent::PersistModelSelection { model: selected_model, effort }
|
||||
if selected_model == replacement && effort == Some(target_effort)
|
||||
);
|
||||
}
|
||||
assert_snapshot!(migration_copies.join("\n"), @r"
|
||||
GPT-5.4 is no longer available
|
||||
|
||||
let persist_selection = rx.try_recv().expect("persist model selection event");
|
||||
assert_matches!(
|
||||
persist_selection,
|
||||
AppEvent::PersistModelSelection { model, effort }
|
||||
if model == "gpt-5.4" && effort == Some(ReasoningEffortConfig::Medium)
|
||||
);
|
||||
Codex now uses GPT-5.6 Terra in place of GPT-5.4. Switch to GPT-5.6 Terra to continue.
|
||||
|
||||
GPT-5.4 Mini is no longer available
|
||||
|
||||
Codex now uses GPT-5.6 Luna in place of GPT-5.4 Mini. Switch to GPT-5.6 Luna to continue.
|
||||
");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn model_migration_prompt_respects_hide_flag_and_self_target() {
|
||||
let presets = all_model_presets();
|
||||
let mut seen = BTreeMap::new();
|
||||
seen.insert("gpt-5.2".to_string(), "gpt-5.4".to_string());
|
||||
seen.insert("gpt-5.4-mini".to_string(), "gpt-5.6-luna".to_string());
|
||||
assert!(!should_show_model_migration_prompt(
|
||||
"gpt-5.2",
|
||||
"gpt-5.4",
|
||||
"gpt-5.4-mini",
|
||||
"gpt-5.6-luna",
|
||||
&seen,
|
||||
&all_model_presets()
|
||||
&presets
|
||||
));
|
||||
assert!(!should_show_model_migration_prompt(
|
||||
"gpt-5.4",
|
||||
"gpt-5.4",
|
||||
"gpt-5.6-luna",
|
||||
"gpt-5.6-luna",
|
||||
&seen,
|
||||
&all_model_presets()
|
||||
&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(),
|
||||
migration_config_key: HIDE_GPT5_1_MIGRATION_PROMPT_CONFIG.to_string(),
|
||||
model_link: None,
|
||||
upgrade_copy: None,
|
||||
migration_markdown: None,
|
||||
retirement_at: None,
|
||||
});
|
||||
available.retain(|preset| preset.model != "gpt-5.2");
|
||||
available.push(current.clone());
|
||||
available.retain(|preset| preset.model != "gpt-5.6-luna");
|
||||
|
||||
assert!(!should_show_model_migration_prompt(
|
||||
¤t.model,
|
||||
"missing-target",
|
||||
"gpt-5.4-mini",
|
||||
"gpt-5.6-luna",
|
||||
&BTreeMap::new(),
|
||||
&available,
|
||||
));
|
||||
|
||||
assert!(target_preset_for_upgrade(&available, "missing-target").is_none());
|
||||
assert!(target_preset_for_upgrade(&available, "gpt-5.6-luna").is_none());
|
||||
|
||||
let mut with_hidden_target = all_model_presets();
|
||||
let target = with_hidden_target
|
||||
.iter_mut()
|
||||
.find(|preset| preset.model == "gpt-5.4")
|
||||
.find(|preset| preset.model == "gpt-5.6-luna")
|
||||
.expect("target preset present");
|
||||
target.show_in_picker = false;
|
||||
|
||||
assert!(!should_show_model_migration_prompt(
|
||||
"gpt-5.2",
|
||||
"gpt-5.4",
|
||||
"gpt-5.4-mini",
|
||||
"gpt-5.6-luna",
|
||||
&BTreeMap::new(),
|
||||
&with_hidden_target,
|
||||
));
|
||||
assert!(target_preset_for_upgrade(&with_hidden_target, "gpt-5.4").is_none());
|
||||
assert!(target_preset_for_upgrade(&with_hidden_target, "gpt-5.6-luna").is_none());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
@@ -21,7 +21,7 @@ async fn model_default_saves_report_server_outcomes_and_target_server_profile()
|
||||
loader_overrides.user_config_profile = Some("work".parse()?);
|
||||
let overrides = if outcome == "overridden" {
|
||||
vec![
|
||||
("model".into(), toml::Value::String("gpt-5.2".into())),
|
||||
("model".into(), toml::Value::String("gpt-5.6-terra".into())),
|
||||
(
|
||||
"model_reasoning_effort".into(),
|
||||
toml::Value::String("low".into()),
|
||||
@@ -69,7 +69,7 @@ async fn model_default_saves_report_server_outcomes_and_target_server_profile()
|
||||
let mut tui = crate::tui::test_support::make_test_tui()?;
|
||||
for event in [
|
||||
AppEvent::PersistModelSelection {
|
||||
model: "gpt-5.4".into(),
|
||||
model: "gpt-5.5".into(),
|
||||
effort: Some(ReasoningEffortConfig::High),
|
||||
},
|
||||
AppEvent::PersistPlanModeReasoningEffort(Some(ReasoningEffortConfig::High)),
|
||||
@@ -77,7 +77,7 @@ async fn model_default_saves_report_server_outcomes_and_target_server_profile()
|
||||
service_tier: Some(ServiceTier::Fast.request_value().into()),
|
||||
},
|
||||
AppEvent::ApplyAdvancedReasoning {
|
||||
model: "gpt-5.4".into(),
|
||||
model: "gpt-5.5".into(),
|
||||
effort: ReasoningEffortConfig::Ultra,
|
||||
},
|
||||
] {
|
||||
@@ -112,7 +112,7 @@ async fn model_default_saves_report_server_outcomes_and_target_server_profile()
|
||||
assert_eq!(
|
||||
toml::from_str::<toml::Value>(&persisted)?,
|
||||
toml::Value::Table(toml::toml! {
|
||||
model = "gpt-5.4"
|
||||
model = "gpt-5.5"
|
||||
model_reasoning_effort = "medium"
|
||||
plan_mode_reasoning_effort = "high"
|
||||
service_tier = "fast"
|
||||
|
||||
@@ -7,6 +7,7 @@ use codex_app_server_client::AppServerEvent;
|
||||
use codex_app_server_protocol::ModelSafetyBufferingUpdatedNotification;
|
||||
use codex_model_provider_info::ModelProviderInfo;
|
||||
use codex_protocol::models::ManagedFileSystemPermissions;
|
||||
use codex_protocol::openai_models::ToolMode;
|
||||
use codex_protocol::permissions::FileSystemAccessMode;
|
||||
use codex_protocol::permissions::FileSystemPath;
|
||||
use codex_protocol::permissions::FileSystemSandboxEntry;
|
||||
@@ -25,8 +26,8 @@ use pretty_assertions::assert_eq;
|
||||
use serde_json::Value;
|
||||
use tokio::sync::oneshot;
|
||||
|
||||
const CURRENT_MODEL: &str = "gpt-5.2";
|
||||
const FASTER_MODEL: &str = "gpt-5.4";
|
||||
const CURRENT_MODEL: &str = "gpt-5.6-terra";
|
||||
const FASTER_MODEL: &str = "gpt-5.6-luna";
|
||||
const MODEL_PROVIDER_ID: &str = "safety-retry-test";
|
||||
const PREVIOUS_PROMPT: &str = "Establish context";
|
||||
const RETRY_PROMPT: &str = "Handle the safety-buffered request";
|
||||
@@ -510,12 +511,25 @@ async fn run_safety_retry(
|
||||
|
||||
let (mut app, mut app_event_rx, _op_rx) = make_test_app_with_channels().await;
|
||||
let codex_home = tempdir()?;
|
||||
// Keep text-only retry fixtures independent of the optional Code Mode host.
|
||||
let mut model_catalog = codex_models_manager::bundled_models_response()?;
|
||||
for model in model_catalog
|
||||
.models
|
||||
.iter_mut()
|
||||
.filter(|model| matches!(model.slug.as_str(), CURRENT_MODEL | FASTER_MODEL))
|
||||
{
|
||||
model.tool_mode = Some(ToolMode::Direct);
|
||||
}
|
||||
let model_catalog_path = codex_home.path().join("models.json");
|
||||
std::fs::write(&model_catalog_path, serde_json::to_vec(&model_catalog)?)?;
|
||||
let model_catalog_path = toml::Value::String(model_catalog_path.display().to_string());
|
||||
std::fs::write(
|
||||
codex_home.path().join("config.toml"),
|
||||
format!(
|
||||
r#"
|
||||
model = "{CURRENT_MODEL}"
|
||||
model_provider = "{MODEL_PROVIDER_ID}"
|
||||
model_catalog_json = {model_catalog_path}
|
||||
|
||||
[model_providers.{MODEL_PROVIDER_ID}]
|
||||
name = "Safety retry test"
|
||||
@@ -533,6 +547,7 @@ goals = true
|
||||
app.config.codex_home = codex_home.path().to_path_buf().abs();
|
||||
app.config.sqlite = codex_state::SqliteConfig::new_for_testing(codex_home.path().abs());
|
||||
app.config.model = Some(CURRENT_MODEL.to_string());
|
||||
app.config.model_catalog = Some(model_catalog);
|
||||
app.config.model_provider_id = MODEL_PROVIDER_ID.to_string();
|
||||
app.config.model_provider = ModelProviderInfo {
|
||||
name: "Safety retry test".to_string(),
|
||||
|
||||
+1
-1
@@ -10,4 +10,4 @@ expression: normalize_snapshot_paths(rendered)
|
||||
|
||||
› Ask Codex to do anything
|
||||
|
||||
gpt-5.2 medium · /tmp/project
|
||||
gpt-5.6-terra medium · /tmp/project
|
||||
|
||||
+1
-2
@@ -1,6 +1,5 @@
|
||||
---
|
||||
source: tui/src/app/tests/backend_banner_fallback_tests.rs
|
||||
assertion_line: 124
|
||||
expression: "notices[0]"
|
||||
---
|
||||
• Automatically switched to gpt-5.2 medium due to usage limits.
|
||||
• Automatically switched to gpt-5.6-terra medium due to usage limits.
|
||||
|
||||
+2
-3
@@ -1,8 +1,7 @@
|
||||
---
|
||||
source: tui/src/app/tests/backend_banner_fallback_tests.rs
|
||||
assertion_line: 234
|
||||
expression: rendered
|
||||
expression: normalize_snapshot_paths(rendered)
|
||||
---
|
||||
› Ask Codex to do anything
|
||||
|
||||
gpt-5.4 default · /tmp/project
|
||||
gpt-5.5 default · /tmp/project
|
||||
|
||||
+1
-1
@@ -2,4 +2,4 @@
|
||||
source: tui/src/app/tests/luna_reserve_recovery_tests.rs
|
||||
expression: notice
|
||||
---
|
||||
• Automatically switched back to gpt-5.4 high because ordinary usage is available again.
|
||||
• Automatically switched back to gpt-5.5 high because ordinary usage is available again.
|
||||
|
||||
+2
-3
@@ -1,11 +1,10 @@
|
||||
---
|
||||
source: tui/src/app/tests/model_defaults_tests.rs
|
||||
assertion_line: 91
|
||||
expression: messages
|
||||
---
|
||||
⚠ Saved default model and reasoning effort, but a higher-priority configuration
|
||||
layer overrides the saved value.
|
||||
• Model changed to gpt-5.4 high
|
||||
• Model changed to gpt-5.5 high
|
||||
⚠ Saved Plan mode reasoning effort, but a higher-priority configuration layer
|
||||
overrides the saved value.
|
||||
⚠ Saved default service tier, but a higher-priority configuration layer
|
||||
@@ -13,4 +12,4 @@ expression: messages
|
||||
• Service tier set to priority
|
||||
⚠ Saved default model and reasoning effort, but a higher-priority configuration
|
||||
layer overrides the saved value.
|
||||
• Model changed to gpt-5.4 ultra for this conversation
|
||||
• Model changed to gpt-5.5 ultra for this conversation
|
||||
|
||||
@@ -44,7 +44,7 @@ async fn cli_fork_omits_implicit_model_and_effort() -> Result<()> {
|
||||
let home = tempdir()?;
|
||||
std::fs::write(
|
||||
home.path().join("config.toml"),
|
||||
"model = \"gpt-5.2\"\nmodel_reasoning_effort = \"low\"\nfeatures.fast_mode = true\n",
|
||||
"model = \"gpt-5.5\"\nmodel_reasoning_effort = \"low\"\nfeatures.fast_mode = true\n",
|
||||
)?;
|
||||
let config = ConfigBuilder::default()
|
||||
.codex_home(home.path().to_path_buf())
|
||||
@@ -76,7 +76,7 @@ async fn cli_fork_omits_implicit_model_and_effort() -> Result<()> {
|
||||
let client_preset = bootstrap
|
||||
.available_models
|
||||
.iter_mut()
|
||||
.find(|preset| preset.model == "gpt-5.2")
|
||||
.find(|preset| preset.model == "gpt-5.5")
|
||||
.expect("client model in catalog");
|
||||
client_preset.upgrade = Some(codex_protocol::openai_models::ModelUpgrade {
|
||||
id: "server-model".into(),
|
||||
@@ -88,7 +88,7 @@ async fn cli_fork_omits_implicit_model_and_effort() -> Result<()> {
|
||||
});
|
||||
assert!(
|
||||
crate::app::startup_prompts::should_show_model_migration_prompt(
|
||||
"gpt-5.2",
|
||||
"gpt-5.5",
|
||||
"server-model",
|
||||
&Default::default(),
|
||||
&bootstrap.available_models,
|
||||
|
||||
@@ -3466,7 +3466,7 @@ mod tests {
|
||||
async fn persisted_resume_does_not_forward_implicit_service_tier() -> Result<()> {
|
||||
let codex_home = tempfile::tempdir().expect("tempdir");
|
||||
let mut config = build_config(&codex_home).await;
|
||||
config.model = Some("gpt-5.4".to_string());
|
||||
config.model = Some("gpt-5.5".to_string());
|
||||
config.service_tier = None;
|
||||
config
|
||||
.features
|
||||
@@ -3486,8 +3486,8 @@ mod tests {
|
||||
let mut app_server = crate::start_embedded_app_server_for_picker(&config).await?;
|
||||
let mut preset = crate::test_support::TEST_MODEL_PRESETS
|
||||
.iter()
|
||||
.find(|preset| preset.model == "gpt-5.4")
|
||||
.expect("gpt-5.4 test preset")
|
||||
.find(|preset| preset.model == "gpt-5.5")
|
||||
.expect("gpt-5.5 test preset")
|
||||
.clone();
|
||||
preset.service_tiers = vec![ModelServiceTier {
|
||||
id: ServiceTier::Fast.request_value().to_string(),
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
source: tui/src/chatwidget/tests/popups_and_settings.rs
|
||||
expression: popup
|
||||
---
|
||||
Select Reasoning Level for gpt-5.4
|
||||
Select Reasoning Level for gpt-5.5
|
||||
|
||||
› 1. More reasoning… Ultra consumes usage limits faster
|
||||
|
||||
|
||||
+2
-3
@@ -5,9 +5,8 @@ expression: "render_bottom_popup(&chat, 80)"
|
||||
Select Model and Effort
|
||||
Access legacy models by running codex -m <model_name> or in your config.toml
|
||||
|
||||
› 1. gpt-5.5 Frontier model for complex coding, research, and real-
|
||||
› 1. gpt-5.6-terra Balanced agentic coding model for everyday work.
|
||||
2. gpt-5.5 (current) Frontier model for complex coding, research, and real-
|
||||
world work.
|
||||
2. gpt-5.2 (current) Optimized for professional work and long-running
|
||||
agents.
|
||||
|
||||
Press enter to confirm or esc to go back
|
||||
|
||||
+1
-1
@@ -7,6 +7,6 @@ expression: "render_bottom_popup(&chat, 80)"
|
||||
|
||||
1. codex-auto-test Auto model
|
||||
› 2. All models (current) Choose a specific model and reasoning level
|
||||
(current: gpt-5.2)
|
||||
(current: gpt-5.5)
|
||||
|
||||
Press enter to confirm or esc to go back
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
source: tui/src/chatwidget/tests/popups_and_settings.rs
|
||||
expression: popup
|
||||
---
|
||||
Select Reasoning Level for gpt-5.4
|
||||
Select Reasoning Level for gpt-5.5
|
||||
|
||||
1. Low Fast responses with lighter reasoning
|
||||
2. Medium (default) Balances speed and reasoning depth for everyday tasks
|
||||
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
---
|
||||
source: tui/src/chatwidget/tests/popups_and_settings.rs
|
||||
expression: popup
|
||||
---
|
||||
Select Reasoning Level for gpt-5.2
|
||||
|
||||
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.
|
||||
|
||||
Press enter to confirm or esc to go back
|
||||
+2
-4
@@ -1,6 +1,6 @@
|
||||
---
|
||||
source: tui/src/chatwidget/tests/popups_and_settings.rs
|
||||
expression: popup
|
||||
expression: "render_bottom_popup(&chat, 80)"
|
||||
---
|
||||
Select Model and Effort
|
||||
Access legacy models by running codex -m <model_name> or in your config.toml
|
||||
@@ -10,9 +10,7 @@ expression: popup
|
||||
2. gpt-5.6-sol Latest frontier agentic coding model.
|
||||
3. gpt-5.6-terra Balanced agentic coding model for everyday work.
|
||||
4. gpt-5.6-luna Fast and affordable agentic coding model.
|
||||
5. gpt-5.5 Frontier model for complex coding, research, and
|
||||
› 5. gpt-5.5 (current) Frontier model for complex coding, research, and
|
||||
real-world work.
|
||||
› 6. gpt-5.2 (current) Optimized for professional work and long-running
|
||||
agents.
|
||||
|
||||
Press enter to confirm or esc to go back
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ expression: "render_bottom_popup(&chat,\n100).replace(&directory,\n\"[[workspace
|
||||
Copy to clipboard
|
||||
|
||||
› 1. Whole status >_ OpenAI Codex (vVERSION)
|
||||
2. Model gpt-5.4
|
||||
2. Model gpt-5.5
|
||||
3. Directory [[workspace]]
|
||||
4. Thread name Clipboard example
|
||||
5. Session ID 00000000-0000-0000-0000-000000000123
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ expression: "whole_status.replace(&directory,\n\"[[workspace]]\").replace(crate:
|
||||
Visit https://chatgpt.com/codex/settings/usage for up-to-date
|
||||
information on rate limits and credits
|
||||
|
||||
Model: gpt-5.4 (reasoning medium, summaries auto)
|
||||
Model: gpt-5.5 (reasoning medium, summaries auto)
|
||||
Model provider: openai
|
||||
Directory: [[workspace]]
|
||||
Permissions: Read Only (Ask for approval)
|
||||
|
||||
@@ -187,7 +187,7 @@ fn open_safety_buffering_retry_confirmation(
|
||||
#[tokio::test]
|
||||
async fn safety_buffering_offers_one_retry_with_app_wording() {
|
||||
let (mut chat, mut rx, mut op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
|
||||
let mut preset = get_available_model(&chat, "gpt-5.4");
|
||||
let mut preset = get_available_model(&chat, "gpt-5.5");
|
||||
preset.model = "faster-model".to_string();
|
||||
preset.display_name = "Faster Model".to_string();
|
||||
chat.model_catalog = Arc::new(ModelCatalog::new(vec![preset]));
|
||||
|
||||
@@ -298,13 +298,23 @@ fn test_model_info(slug: &str, priority: i32, supports_fast_mode: bool) -> Model
|
||||
}
|
||||
|
||||
pub(crate) fn set_fast_mode_test_catalog(chat: &mut ChatWidget) {
|
||||
set_fast_mode_test_catalog_for_models(chat, "gpt-5.4", "gpt-5.2");
|
||||
}
|
||||
|
||||
pub(crate) fn set_fast_mode_test_catalog_for_models(
|
||||
chat: &mut ChatWidget,
|
||||
fast_model: &str,
|
||||
standard_model: &str,
|
||||
) {
|
||||
let models: Vec<ModelPreset> = ModelsResponse {
|
||||
models: vec![
|
||||
test_model_info(
|
||||
"gpt-5.4", /*priority*/ 0, /*supports_fast_mode*/ true,
|
||||
fast_model, /*priority*/ 0, /*supports_fast_mode*/ true,
|
||||
),
|
||||
test_model_info(
|
||||
"gpt-5.2", /*priority*/ 1, /*supports_fast_mode*/ false,
|
||||
standard_model,
|
||||
/*priority*/ 1,
|
||||
/*supports_fast_mode*/ false,
|
||||
),
|
||||
],
|
||||
}
|
||||
|
||||
@@ -259,7 +259,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.4")).await;
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.5")).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())
|
||||
@@ -269,7 +269,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.4");
|
||||
let preset = get_available_model(&chat, "gpt-5.5");
|
||||
chat.open_reasoning_popup(preset);
|
||||
chat.handle_key_event(KeyEvent::from(KeyCode::Down));
|
||||
chat.handle_key_event(KeyEvent::from(KeyCode::Enter));
|
||||
@@ -280,13 +280,13 @@ async fn reasoning_selection_in_plan_mode_opens_scope_prompt_event() {
|
||||
AppEvent::OpenPlanReasoningScopePrompt {
|
||||
model,
|
||||
effort: Some(_)
|
||||
} if model == "gpt-5.4"
|
||||
} if model == "gpt-5.5"
|
||||
);
|
||||
}
|
||||
|
||||
#[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.4")).await;
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.5")).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())
|
||||
@@ -297,7 +297,7 @@ async fn reasoning_selection_in_plan_mode_without_effort_change_does_not_open_sc
|
||||
|
||||
chat.set_reasoning_effort(Some(ReasoningEffortConfig::Medium));
|
||||
|
||||
let preset = get_available_model(&chat, "gpt-5.4");
|
||||
let preset = get_available_model(&chat, "gpt-5.5");
|
||||
chat.open_reasoning_popup(preset);
|
||||
chat.handle_key_event(KeyEvent::from(KeyCode::Enter));
|
||||
|
||||
@@ -305,7 +305,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.4"
|
||||
AppEvent::UpdateModel(model) if model == "gpt-5.5"
|
||||
)),
|
||||
"expected model update event; events: {events:?}"
|
||||
);
|
||||
@@ -320,7 +320,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.4")).await;
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.5")).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())
|
||||
@@ -334,7 +334,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.4");
|
||||
let preset = get_available_model(&chat, "gpt-5.5");
|
||||
chat.open_reasoning_popup(preset);
|
||||
chat.handle_key_event(KeyEvent::from(KeyCode::Enter));
|
||||
|
||||
@@ -344,13 +344,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.4"
|
||||
} if model == "gpt-5.5"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn reasoning_shortcut_in_plan_mode_updates_plan_override_without_prompt_or_persist() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.4")).await;
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.5")).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())
|
||||
@@ -398,7 +398,7 @@ async fn reasoning_shortcut_in_plan_mode_updates_plan_override_without_prompt_or
|
||||
#[tokio::test]
|
||||
async fn advanced_reasoning_selection_in_plan_mode_uses_expected_scope() {
|
||||
for effort in [ReasoningEffortConfig::Ultra, ReasoningEffortConfig::Max] {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.4")).await;
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.5")).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())
|
||||
@@ -406,7 +406,7 @@ async fn advanced_reasoning_selection_in_plan_mode_uses_expected_scope() {
|
||||
chat.set_collaboration_mask(plan_mask);
|
||||
let _ = drain_insert_history(&mut rx);
|
||||
|
||||
let mut preset = get_available_model(&chat, "gpt-5.4");
|
||||
let mut preset = get_available_model(&chat, "gpt-5.5");
|
||||
preset.supported_reasoning_efforts = vec![ReasoningEffortPreset {
|
||||
effort: effort.clone(),
|
||||
description: "Advanced reasoning".to_string(),
|
||||
@@ -421,7 +421,7 @@ async fn advanced_reasoning_selection_in_plan_mode_uses_expected_scope() {
|
||||
AppEvent::ApplyAdvancedReasoning {
|
||||
model,
|
||||
effort: ReasoningEffortConfig::Ultra,
|
||||
} if model == "gpt-5.4"
|
||||
} if model == "gpt-5.5"
|
||||
)));
|
||||
assert!(events.iter().all(|event| !matches!(
|
||||
event,
|
||||
@@ -435,7 +435,7 @@ async fn advanced_reasoning_selection_in_plan_mode_uses_expected_scope() {
|
||||
AppEvent::OpenPlanReasoningScopePrompt {
|
||||
model,
|
||||
effort: Some(ReasoningEffortConfig::Max),
|
||||
} if model == "gpt-5.4"
|
||||
} if model == "gpt-5.5"
|
||||
)));
|
||||
}
|
||||
}
|
||||
@@ -443,7 +443,7 @@ async fn advanced_reasoning_selection_in_plan_mode_uses_expected_scope() {
|
||||
|
||||
#[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.4")).await;
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.5")).await;
|
||||
chat.set_feature_enabled(Feature::CollaborationModes, /*enabled*/ true);
|
||||
set_chatgpt_auth(&mut chat);
|
||||
chat.set_reasoning_effort(Some(ReasoningEffortConfig::High));
|
||||
@@ -453,7 +453,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.4");
|
||||
let preset = get_available_model(&chat, "gpt-5.5");
|
||||
chat.open_reasoning_popup(preset);
|
||||
|
||||
let popup = render_bottom_popup(&chat, /*width*/ 100);
|
||||
@@ -466,7 +466,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.4")).await;
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.5")).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())
|
||||
@@ -475,7 +475,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.2");
|
||||
let preset = get_available_model(&chat, "gpt-5.6-terra");
|
||||
chat.open_reasoning_popup(preset);
|
||||
chat.handle_key_event(KeyEvent::from(KeyCode::Enter));
|
||||
|
||||
@@ -483,7 +483,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.2"
|
||||
AppEvent::UpdateModel(model) if model == "gpt-5.6-terra"
|
||||
)),
|
||||
"expected model update event; events: {events:?}"
|
||||
);
|
||||
@@ -497,8 +497,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.4")).await;
|
||||
chat.open_plan_reasoning_scope_prompt("gpt-5.4".to_string(), Some(ReasoningEffortConfig::High));
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.5")).await;
|
||||
chat.open_plan_reasoning_scope_prompt("gpt-5.5".to_string(), Some(ReasoningEffortConfig::High));
|
||||
|
||||
chat.handle_key_event(KeyEvent::from(KeyCode::Down));
|
||||
chat.handle_key_event(KeyEvent::from(KeyCode::Enter));
|
||||
@@ -522,7 +522,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.4"
|
||||
if model == "gpt-5.5"
|
||||
)),
|
||||
"expected global model reasoning selection persistence; events: {events:?}"
|
||||
);
|
||||
@@ -548,7 +548,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.4")).await;
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.5")).await;
|
||||
chat.local_settings.tui.notification_settings.notifications =
|
||||
Notifications::Custom(vec!["plan-mode-prompt".to_string()]);
|
||||
|
||||
@@ -562,11 +562,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.4")).await;
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.5")).await;
|
||||
chat.local_settings.tui.notification_settings.notifications =
|
||||
Notifications::Custom(vec!["plan-mode-prompt".to_string()]);
|
||||
|
||||
chat.open_plan_reasoning_scope_prompt("gpt-5.4".to_string(), Some(ReasoningEffortConfig::High));
|
||||
chat.open_plan_reasoning_scope_prompt("gpt-5.5".to_string(), Some(ReasoningEffortConfig::High));
|
||||
|
||||
assert_matches!(
|
||||
chat.pending_notification,
|
||||
@@ -576,7 +576,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.4")).await;
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.5")).await;
|
||||
|
||||
chat.open_plan_implementation_prompt();
|
||||
chat.notify(Notification::AgentTurnComplete {
|
||||
@@ -591,7 +591,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.4")).await;
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.5")).await;
|
||||
|
||||
chat.notify(Notification::AgentTurnComplete {
|
||||
response: "done".to_string(),
|
||||
@@ -623,7 +623,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.4")).await;
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.5")).await;
|
||||
chat.local_settings.tui.notification_settings.notifications =
|
||||
Notifications::Custom(vec!["plan-mode-prompt".to_string()]);
|
||||
|
||||
@@ -654,10 +654,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.4")).await;
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.5")).await;
|
||||
chat.set_plan_mode_reasoning_effort(Some(ReasoningEffortConfig::Low));
|
||||
chat.open_plan_reasoning_scope_prompt(
|
||||
"gpt-5.4".to_string(),
|
||||
"gpt-5.5".to_string(),
|
||||
Some(ReasoningEffortConfig::Medium),
|
||||
);
|
||||
|
||||
@@ -671,9 +671,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.4")).await;
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.5")).await;
|
||||
chat.open_plan_reasoning_scope_prompt(
|
||||
"gpt-5.4".to_string(),
|
||||
"gpt-5.5".to_string(),
|
||||
Some(ReasoningEffortConfig::Medium),
|
||||
);
|
||||
|
||||
@@ -683,8 +683,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.4")).await;
|
||||
chat.open_plan_reasoning_scope_prompt("gpt-5.4".to_string(), Some(ReasoningEffortConfig::High));
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.5")).await;
|
||||
chat.open_plan_reasoning_scope_prompt("gpt-5.5".to_string(), Some(ReasoningEffortConfig::High));
|
||||
|
||||
chat.handle_key_event(KeyEvent::from(KeyCode::Enter));
|
||||
|
||||
@@ -1365,7 +1365,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.4-mini".to_string());
|
||||
plan_mask.model = Some("gpt-5.6-luna".to_string());
|
||||
chat.set_collaboration_mask(plan_mask);
|
||||
|
||||
let plan_messages = drain_insert_history(&mut rx)
|
||||
@@ -1374,7 +1374,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.4-mini medium for Plan mode."),
|
||||
plan_messages.contains("Model changed to gpt-5.6-luna medium for Plan mode."),
|
||||
"expected Plan-mode model switch notice, got: {plan_messages:?}"
|
||||
);
|
||||
|
||||
@@ -1397,7 +1397,7 @@ async fn mode_switch_surfaces_model_change_notification_when_effective_model_cha
|
||||
|
||||
#[tokio::test]
|
||||
async fn mode_switch_surfaces_reasoning_change_notification_when_model_stays_same() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.2")).await;
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.6-terra")).await;
|
||||
chat.set_feature_enabled(Feature::CollaborationModes, /*enabled*/ true);
|
||||
chat.set_reasoning_effort(Some(ReasoningEffortConfig::High));
|
||||
|
||||
@@ -1411,7 +1411,7 @@ async fn mode_switch_surfaces_reasoning_change_notification_when_model_stays_sam
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
assert!(
|
||||
plan_messages.contains("Model changed to gpt-5.2 medium for Plan mode."),
|
||||
plan_messages.contains("Model changed to gpt-5.6-terra medium for Plan mode."),
|
||||
"expected reasoning-change notice in Plan mode, got: {plan_messages:?}"
|
||||
);
|
||||
}
|
||||
@@ -1657,21 +1657,21 @@ async fn make_startup_chat_with_cli_overrides(
|
||||
|
||||
#[tokio::test]
|
||||
async fn set_model_updates_active_collaboration_mask() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.2")).await;
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.6-terra")).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.4-mini");
|
||||
chat.set_model("gpt-5.6-luna");
|
||||
|
||||
assert_eq!(chat.current_model(), "gpt-5.4-mini");
|
||||
assert_eq!(chat.current_model(), "gpt-5.6-luna");
|
||||
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.2")).await;
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.6-terra")).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");
|
||||
@@ -1688,7 +1688,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.2")).await;
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.6-terra")).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)
|
||||
@@ -1758,10 +1758,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.4")).await;
|
||||
let (mut chat, _rx, mut op_rx) = make_chatwidget_manual(Some("gpt-5.5")).await;
|
||||
chat.set_feature_enabled(Feature::Personality, /*enabled*/ true);
|
||||
chat.thread_id = Some(ThreadId::new());
|
||||
chat.set_model("gpt-5.4");
|
||||
chat.set_model("gpt-5.5");
|
||||
chat.set_personality(Personality::Friendly);
|
||||
|
||||
chat.bottom_pane
|
||||
|
||||
@@ -3302,7 +3302,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.2")).await;
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.5")).await;
|
||||
chat.thread_id = Some(ThreadId::new());
|
||||
chat.open_model_popup();
|
||||
|
||||
@@ -3317,7 +3317,7 @@ fn apply_model_list_response(chat: &mut ChatWidget, presets: Vec<ModelPreset>) {
|
||||
|
||||
#[tokio::test]
|
||||
async fn model_picker_refresh_rejects_obsolete_and_unusable_replies() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.2")).await;
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.5")).await;
|
||||
chat.thread_id = Some(ThreadId::new());
|
||||
let initial = chat.model_catalog.try_list_models().unwrap();
|
||||
let mut refreshed = initial.clone();
|
||||
@@ -3362,9 +3362,9 @@ async fn model_picker_refresh_rejects_obsolete_and_unusable_replies() {
|
||||
#[tokio::test]
|
||||
async fn model_picker_refreshes_startup_catalog() {
|
||||
for (explicit_all_models, hidden_startup) in [(false, false), (true, false), (false, true)] {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.2")).await;
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.5")).await;
|
||||
chat.thread_id = Some(ThreadId::new());
|
||||
let mut startup = vec![get_available_model(&chat, "gpt-5.2")];
|
||||
let mut startup = vec![get_available_model(&chat, "gpt-5.5")];
|
||||
let mut refreshed = chat.model_catalog.try_list_models().unwrap();
|
||||
let mut auto = startup[0].clone();
|
||||
auto.model = "codex-auto-test".to_string();
|
||||
@@ -3405,10 +3405,10 @@ async fn model_picker_refreshes_startup_catalog() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn model_picker_queued_all_models_uses_refreshed_catalog() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.2")).await;
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.5")).await;
|
||||
chat.thread_id = Some(ThreadId::new());
|
||||
let refreshed = chat.model_catalog.try_list_models().unwrap();
|
||||
let preset = get_available_model(&chat, "gpt-5.2");
|
||||
let preset = get_available_model(&chat, "gpt-5.5");
|
||||
let mut auto = preset.clone();
|
||||
auto.model = "codex-auto-test".to_string();
|
||||
auto.id = auto.model.clone();
|
||||
@@ -3430,16 +3430,16 @@ async fn model_picker_queued_all_models_uses_refreshed_catalog() {
|
||||
#[tokio::test]
|
||||
async fn model_picker_refresh_preserves_highlight() {
|
||||
for (remove_selected, reasoning_submenu, expected) in [
|
||||
(false, false, "gpt-5.5"),
|
||||
(true, false, "gpt-5.2"),
|
||||
(false, true, "gpt-5.5"),
|
||||
(true, true, "gpt-5.2"),
|
||||
(false, false, "gpt-5.6-terra"),
|
||||
(true, false, "gpt-5.5"),
|
||||
(false, true, "gpt-5.6-terra"),
|
||||
(true, true, "gpt-5.5"),
|
||||
] {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.2")).await;
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.5")).await;
|
||||
chat.thread_id = Some(ThreadId::new());
|
||||
let mut presets = vec![
|
||||
get_available_model(&chat, "gpt-5.2"),
|
||||
get_available_model(&chat, "gpt-5.5"),
|
||||
get_available_model(&chat, "gpt-5.6-terra"),
|
||||
];
|
||||
chat.model_catalog = Arc::new(ModelCatalog::new(presets.clone()));
|
||||
chat.open_model_popup();
|
||||
@@ -3506,9 +3506,9 @@ async fn model_picker_refresh_preserves_dismissal_and_reasoning_submenu() {
|
||||
(false, false, true),
|
||||
(false, true, true),
|
||||
] {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.2")).await;
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.5")).await;
|
||||
chat.thread_id = Some(ThreadId::new());
|
||||
let preset = get_available_model(&chat, "gpt-5.2");
|
||||
let preset = get_available_model(&chat, "gpt-5.5");
|
||||
let refreshed = chat.model_catalog.try_list_models().unwrap();
|
||||
chat.model_catalog = Arc::new(ModelCatalog::new(vec![preset.clone()]));
|
||||
chat.open_model_popup();
|
||||
@@ -3556,9 +3556,9 @@ async fn model_picker_refresh_dismisses_empty_choices() {
|
||||
for (explicit_all_models, reasoning_submenu) in
|
||||
[(false, false), (true, false), (false, true), (true, true)]
|
||||
{
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.2")).await;
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.5")).await;
|
||||
chat.thread_id = Some(ThreadId::new());
|
||||
let mut preset = get_available_model(&chat, "gpt-5.2");
|
||||
let mut preset = get_available_model(&chat, "gpt-5.5");
|
||||
chat.open_model_popup();
|
||||
if explicit_all_models {
|
||||
chat.handle_key_event(KeyEvent::from(KeyCode::Esc));
|
||||
@@ -3592,7 +3592,7 @@ async fn model_picker_refresh_dismisses_empty_choices() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn personality_selection_popup_snapshot() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.4")).await;
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.5")).await;
|
||||
chat.thread_id = Some(ThreadId::new());
|
||||
chat.open_personality_popup();
|
||||
|
||||
@@ -3687,12 +3687,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.4")).await;
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.5")).await;
|
||||
|
||||
set_chatgpt_auth(&mut chat);
|
||||
chat.set_reasoning_effort(Some(ReasoningEffortConfig::High));
|
||||
|
||||
let mut preset = get_available_model(&chat, "gpt-5.4");
|
||||
let mut preset = get_available_model(&chat, "gpt-5.5");
|
||||
preset.supported_reasoning_efforts.insert(
|
||||
2,
|
||||
ReasoningEffortPreset {
|
||||
@@ -3720,10 +3720,10 @@ async fn model_reasoning_selection_popup_snapshot() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn model_advanced_reasoning_selection_popup_snapshot() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.4")).await;
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.5")).await;
|
||||
chat.set_reasoning_effort(Some(ReasoningEffortConfig::Ultra));
|
||||
|
||||
let mut preset = get_available_model(&chat, "gpt-5.4");
|
||||
let mut preset = get_available_model(&chat, "gpt-5.5");
|
||||
preset.supported_reasoning_efforts.extend([
|
||||
ReasoningEffortPreset {
|
||||
effort: ReasoningEffortConfig::Ultra,
|
||||
@@ -3742,11 +3742,11 @@ async fn model_advanced_reasoning_selection_popup_snapshot() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn model_reasoning_selection_popup_applies_custom_effort() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.4")).await;
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.5")).await;
|
||||
let custom_effort = ReasoningEffortConfig::Custom("future".to_string());
|
||||
chat.set_reasoning_effort(Some(ReasoningEffortConfig::XHigh));
|
||||
|
||||
let mut preset = get_available_model(&chat, "gpt-5.4");
|
||||
let mut preset = get_available_model(&chat, "gpt-5.5");
|
||||
preset
|
||||
.supported_reasoning_efforts
|
||||
.push(ReasoningEffortPreset {
|
||||
@@ -3770,19 +3770,19 @@ async fn model_reasoning_selection_popup_applies_custom_effort() {
|
||||
selected_effort_events,
|
||||
vec![
|
||||
(None, Some(custom_effort.clone())),
|
||||
(Some("gpt-5.4".to_string()), Some(custom_effort)),
|
||||
(Some("gpt-5.5".to_string()), Some(custom_effort)),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
async fn select_ultra_with_multi_agent_thread_limit(max_threads: usize) -> (bool, Vec<String>) {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.4")).await;
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.5")).await;
|
||||
chat.config
|
||||
.multi_agent_v2
|
||||
.max_concurrent_threads_per_session = max_threads;
|
||||
chat.set_reasoning_effort(Some(ReasoningEffortConfig::High));
|
||||
|
||||
let mut preset = get_available_model(&chat, "gpt-5.4");
|
||||
let mut preset = get_available_model(&chat, "gpt-5.5");
|
||||
preset.default_reasoning_effort = ReasoningEffortConfig::High;
|
||||
preset.supported_reasoning_efforts = vec![
|
||||
ReasoningEffortPreset {
|
||||
@@ -3849,10 +3849,10 @@ async fn ultra_reasoning_selection_skips_warning_below_threshold() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn max_reasoning_selection_persists_model_selection() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.4")).await;
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.5")).await;
|
||||
chat.set_reasoning_effort(Some(ReasoningEffortConfig::High));
|
||||
|
||||
let mut preset = get_available_model(&chat, "gpt-5.4");
|
||||
let mut preset = get_available_model(&chat, "gpt-5.5");
|
||||
preset.supported_reasoning_efforts = vec![ReasoningEffortPreset {
|
||||
effort: ReasoningEffortConfig::Max,
|
||||
description: "Maximum reasoning".to_string(),
|
||||
@@ -3870,7 +3870,7 @@ async fn max_reasoning_selection_persists_model_selection() {
|
||||
AppEvent::PersistModelSelection {
|
||||
model,
|
||||
effort: Some(ReasoningEffortConfig::Max),
|
||||
} if model == "gpt-5.4"
|
||||
} if model == "gpt-5.5"
|
||||
)));
|
||||
assert!(
|
||||
events
|
||||
@@ -3879,26 +3879,12 @@ async fn max_reasoning_selection_persists_model_selection() {
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn model_reasoning_selection_popup_extra_high_warning_snapshot() {
|
||||
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.2");
|
||||
chat.open_reasoning_popup(preset);
|
||||
|
||||
let popup = render_bottom_popup(&chat, /*width*/ 80);
|
||||
assert_chatwidget_snapshot!("model_reasoning_selection_popup_extra_high_warning", popup);
|
||||
}
|
||||
|
||||
async fn assert_reasoning_shortcuts_update_effort(
|
||||
key_events: [KeyEvent; 2],
|
||||
expected_effort: ReasoningEffortConfig,
|
||||
) {
|
||||
for key_event in key_events {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.4")).await;
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.5")).await;
|
||||
chat.thread_id = Some(ThreadId::new());
|
||||
chat.set_reasoning_effort(Some(ReasoningEffortConfig::Medium));
|
||||
|
||||
@@ -3953,7 +3939,7 @@ async fn reasoning_down_shortcuts_lower_reasoning_effort() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn reasoning_shortcut_clears_armed_quit_shortcut() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.4")).await;
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.5")).await;
|
||||
chat.thread_id = Some(ThreadId::new());
|
||||
chat.set_reasoning_effort(Some(ReasoningEffortConfig::Medium));
|
||||
chat.arm_quit_shortcut(key_hint::ctrl(KeyCode::Char('c')));
|
||||
@@ -3974,7 +3960,7 @@ async fn reasoning_shortcut_clears_armed_quit_shortcut() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn reasoning_shortcut_is_ignored_with_model_popup_open() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.4")).await;
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.5")).await;
|
||||
chat.thread_id = Some(ThreadId::new());
|
||||
chat.set_reasoning_effort(Some(ReasoningEffortConfig::Medium));
|
||||
chat.open_model_popup();
|
||||
@@ -3999,12 +3985,12 @@ async fn reasoning_shortcut_is_ignored_with_model_popup_open() {
|
||||
#[tokio::test]
|
||||
async fn reasoning_up_shortcut_does_not_silently_enter_advanced_effort() {
|
||||
for (model, model_path) in [
|
||||
("gpt-5.4", "All models → gpt-5.4"),
|
||||
("gpt-5.5", "All models → gpt-5.5"),
|
||||
("codex-auto-test", "codex-auto-test"),
|
||||
] {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.4")).await;
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.5")).await;
|
||||
chat.thread_id = Some(ThreadId::new());
|
||||
let mut preset = get_available_model(&chat, "gpt-5.4");
|
||||
let mut preset = get_available_model(&chat, "gpt-5.5");
|
||||
preset.id = model.to_string();
|
||||
preset.model = model.to_string();
|
||||
preset.display_name = model.to_string();
|
||||
@@ -4051,9 +4037,9 @@ async fn reasoning_up_shortcut_does_not_silently_enter_advanced_effort() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn reasoning_down_shortcut_can_leave_advanced_effort() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.4")).await;
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.5")).await;
|
||||
chat.thread_id = Some(ThreadId::new());
|
||||
let mut preset = get_available_model(&chat, "gpt-5.4");
|
||||
let mut preset = get_available_model(&chat, "gpt-5.5");
|
||||
preset.supported_reasoning_efforts.extend([
|
||||
ReasoningEffortPreset {
|
||||
effort: ReasoningEffortConfig::Ultra,
|
||||
@@ -4088,11 +4074,11 @@ async fn reasoning_down_shortcut_can_leave_advanced_effort() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn reasoning_popup_shows_extra_high_with_space() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.4")).await;
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.5")).await;
|
||||
|
||||
set_chatgpt_auth(&mut chat);
|
||||
|
||||
let preset = get_available_model(&chat, "gpt-5.4");
|
||||
let preset = get_available_model(&chat, "gpt-5.5");
|
||||
chat.open_reasoning_popup(preset);
|
||||
|
||||
let popup = render_bottom_popup(&chat, /*width*/ 120);
|
||||
@@ -4158,7 +4144,7 @@ async fn single_reasoning_option_skips_selection() {
|
||||
#[tokio::test]
|
||||
async fn advanced_only_reasoning_option_requires_explicit_selection() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
|
||||
let mut preset = get_available_model(&chat, "gpt-5.4");
|
||||
let mut preset = get_available_model(&chat, "gpt-5.5");
|
||||
preset.default_reasoning_effort = ReasoningEffortConfig::Ultra;
|
||||
preset.supported_reasoning_efforts = vec![ReasoningEffortPreset {
|
||||
effort: ReasoningEffortConfig::Ultra,
|
||||
@@ -4264,11 +4250,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.4")).await;
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.5")).await;
|
||||
chat.thread_id = Some(ThreadId::new());
|
||||
chat.open_model_popup();
|
||||
|
||||
let preset = get_available_model(&chat, "gpt-5.4");
|
||||
let preset = get_available_model(&chat, "gpt-5.5");
|
||||
chat.open_reasoning_popup(preset);
|
||||
|
||||
let before_escape = render_bottom_popup(&chat, /*width*/ 80);
|
||||
|
||||
@@ -122,8 +122,8 @@ fn next_copy_selection(
|
||||
|
||||
#[tokio::test]
|
||||
async fn service_tier_commands_lowercase_catalog_names() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.4")).await;
|
||||
let mut preset = get_available_model(&chat, "gpt-5.4");
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.5")).await;
|
||||
let mut preset = get_available_model(&chat, "gpt-5.5");
|
||||
let expected_description = preset
|
||||
.service_tiers
|
||||
.iter()
|
||||
@@ -2068,7 +2068,7 @@ async fn slash_copy_picker_previews_whole_response_code_blocks_and_blockquotes()
|
||||
|
||||
#[tokio::test]
|
||||
async fn slash_copy_picker_copies_status_fields_and_preserves_source_after_copying() {
|
||||
let (mut chat, mut rx, mut op_rx) = make_chatwidget_manual(Some("gpt-5.4")).await;
|
||||
let (mut chat, mut rx, mut op_rx) = make_chatwidget_manual(Some("gpt-5.5")).await;
|
||||
let session_id = "00000000-0000-0000-0000-000000000123";
|
||||
chat.thread_id = Some(ThreadId::from_string(session_id).expect("valid thread ID"));
|
||||
chat.thread_name = Some("Clipboard example".to_string());
|
||||
@@ -2098,7 +2098,7 @@ async fn slash_copy_picker_copies_status_fields_and_preserves_source_after_copyi
|
||||
);
|
||||
let expected = [
|
||||
("Whole status", whole_status.as_str()),
|
||||
("Model", "gpt-5.4"),
|
||||
("Model", "gpt-5.5"),
|
||||
("Directory", directory.as_str()),
|
||||
("Thread name", "Clipboard example"),
|
||||
("Session ID", session_id),
|
||||
|
||||
@@ -90,7 +90,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.4")).await;
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.5")).await;
|
||||
chat.config.model_reasoning_effort = None;
|
||||
|
||||
chat.dispatch_command(SlashCommand::Status);
|
||||
@@ -102,7 +102,7 @@ async fn status_command_uses_catalog_default_reasoning_when_config_empty() {
|
||||
other => panic!("expected status output, got {other:?}"),
|
||||
};
|
||||
assert!(
|
||||
rendered.contains("gpt-5.4 (reasoning medium, summaries auto)"),
|
||||
rendered.contains("gpt-5.5 (reasoning medium, summaries auto)"),
|
||||
"expected /status to render the catalog default reasoning effort, got: {rendered}"
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user