Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 30 additions & 25 deletions codex-rs/tui/src/app/startup_prompts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,28 @@ pub(super) fn target_preset_for_upgrade<'a>(
.find(|preset| preset.model == target_model && preset.show_in_picker)
}

pub(super) fn apply_accepted_model_migration(
config: &mut Config,
app_event_tx: &AppEventSender,
from_model: String,
target_model: String,
target_default_effort: ReasoningEffortConfig,
) {
app_event_tx.send(AppEvent::PersistModelMigrationPromptAcknowledged {
from_model,
to_model: target_model.clone(),
});

config.model = Some(target_model.clone());
config.model_reasoning_effort = Some(target_default_effort);
app_event_tx.send(AppEvent::UpdateModel(target_model.clone()));
app_event_tx.send(AppEvent::UpdateReasoningEffort(Some(target_default_effort)));
app_event_tx.send(AppEvent::PersistModelSelection {
model: target_model,
effort: Some(target_default_effort),
});
}
Comment thread
shijie-oai marked this conversation as resolved.

pub(super) const MODEL_AVAILABILITY_NUX_MAX_SHOW_COUNT: u32 = 4;

#[derive(Debug, Clone, PartialEq, Eq)]
Expand Down Expand Up @@ -218,7 +240,7 @@ pub(super) async fn handle_model_migration_prompt_if_needed(

if let Some(ModelUpgrade {
id: target_model,
reasoning_effort_mapping,
reasoning_effort_mapping: _,
migration_config_key,
model_link,
upgrade_copy,
Expand Down Expand Up @@ -263,30 +285,13 @@ pub(super) async fn handle_model_migration_prompt_if_needed(
);
match run_model_migration_prompt(tui, prompt_copy).await {
ModelMigrationOutcome::Accepted => {
app_event_tx.send(AppEvent::PersistModelMigrationPromptAcknowledged {
from_model: model.to_string(),
to_model: target_model.clone(),
});

let mapped_effort = if let Some(reasoning_effort_mapping) = reasoning_effort_mapping
&& let Some(reasoning_effort) = config.model_reasoning_effort
{
reasoning_effort_mapping
.get(&reasoning_effort)
.cloned()
.or(config.model_reasoning_effort)
} else {
config.model_reasoning_effort
};

config.model = Some(target_model.clone());
config.model_reasoning_effort = mapped_effort;
app_event_tx.send(AppEvent::UpdateModel(target_model.clone()));
app_event_tx.send(AppEvent::UpdateReasoningEffort(mapped_effort));
app_event_tx.send(AppEvent::PersistModelSelection {
model: target_model.clone(),
effort: mapped_effort,
});
apply_accepted_model_migration(
config,
app_event_tx,
model.to_string(),
target_model.clone(),
target_preset.default_reasoning_effort,
);
}
ModelMigrationOutcome::Rejected => {
app_event_tx.send(AppEvent::PersistModelMigrationPromptAcknowledged {
Expand Down
57 changes: 57 additions & 0 deletions codex-rs/tui/src/app/tests/model_catalog.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use super::*;
use assert_matches::assert_matches;
use codex_config::types::ModelAvailabilityNuxConfig;
use codex_protocol::openai_models::ModelAvailabilityNux;
use pretty_assertions::assert_eq;
use tokio::sync::mpsc::unbounded_channel;

fn all_model_presets() -> Vec<ModelPreset> {
crate::legacy_core::test_support::all_model_presets().clone()
Expand Down Expand Up @@ -172,6 +174,61 @@ fn select_model_availability_nux_returns_none_when_all_models_are_exhausted() {
assert_eq!(selected, None);
}

#[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);

let (tx_raw, mut rx) = unbounded_channel();
let app_event_tx = AppEventSender::new(tx_raw);

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("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 == "gpt-5.2" && to_model == "gpt-5.4"
);

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(ReasoningEffortConfig::Medium))
);

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)
);
}

#[tokio::test]
async fn model_migration_prompt_respects_hide_flag_and_self_target() {
let mut seen = BTreeMap::new();
Expand Down
5 changes: 3 additions & 2 deletions codex-rs/tui/src/app/thread_session_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ mod tests {
approval_policy: AskForApproval::Never,
approvals_reviewer: ApprovalsReviewer::User,
sandbox_policy: SandboxPolicy::new_read_only_policy(),
permission_profile: None,
cwd: cwd.abs(),
instruction_source_paths: Vec::new(),
reasoning_effort: None,
Expand Down Expand Up @@ -155,7 +156,7 @@ mod tests {
.insert(side_thread_id, SideThreadState::new(main_thread_id));
app.config.permissions.approval_policy =
codex_config::Constrained::allow_any(AskForApproval::OnRequest);
app.config.approvals_reviewer = ApprovalsReviewer::GuardianSubagent;
app.config.approvals_reviewer = ApprovalsReviewer::AutoReview;
app.config.permissions.sandbox_policy =
codex_config::Constrained::allow_any(SandboxPolicy::new_workspace_write_policy());

Expand All @@ -164,7 +165,7 @@ mod tests {

let expected_main_session = ThreadSessionState {
approval_policy: AskForApproval::OnRequest,
approvals_reviewer: ApprovalsReviewer::GuardianSubagent,
approvals_reviewer: ApprovalsReviewer::AutoReview,
sandbox_policy: SandboxPolicy::new_workspace_write_policy(),
..main_session
};
Expand Down
Loading