mirror of
https://github.com/openai/codex.git
synced 2026-10-30 19:18:49 +00:00
models
This commit is contained in:
@@ -238,6 +238,11 @@ impl App {
|
||||
}
|
||||
};
|
||||
|
||||
// Startup-only: show any previously scheduled model migration notice, then refresh the
|
||||
// schedule to reflect the current config + model presets (at most one scheduled notice).
|
||||
chat_widget.maybe_show_pending_model_migration_notice();
|
||||
chat_widget.refresh_pending_model_migration_notice();
|
||||
|
||||
chat_widget.maybe_prompt_windows_sandbox_enable();
|
||||
|
||||
let file_search = FileSearchManager::new(config.cwd.clone(), app_event_tx.clone());
|
||||
|
||||
@@ -453,7 +453,6 @@ impl ChatWidget {
|
||||
event,
|
||||
self.show_welcome_banner,
|
||||
));
|
||||
self.maybe_show_pending_model_migration_notice();
|
||||
if let Some(messages) = initial_messages {
|
||||
self.replay_initial_messages(messages);
|
||||
}
|
||||
@@ -470,7 +469,6 @@ impl ChatWidget {
|
||||
// turn now that we know the session is configured.
|
||||
self.maybe_send_next_queued_input();
|
||||
}
|
||||
self.maybe_schedule_model_migration_notice(&model_for_header);
|
||||
if !self.suppress_session_configured_redraw {
|
||||
self.request_redraw();
|
||||
}
|
||||
@@ -808,7 +806,7 @@ impl ChatWidget {
|
||||
self.request_redraw();
|
||||
}
|
||||
|
||||
fn maybe_show_pending_model_migration_notice(&mut self) {
|
||||
pub(crate) fn maybe_show_pending_model_migration_notice(&mut self) {
|
||||
let Some(notice) = model_migration::take_pending_model_migration_notice(&mut self.config)
|
||||
else {
|
||||
return;
|
||||
@@ -828,17 +826,13 @@ impl ChatWidget {
|
||||
});
|
||||
}
|
||||
|
||||
fn maybe_schedule_model_migration_notice(&self, model: &str) {
|
||||
pub(crate) fn refresh_pending_model_migration_notice(&self) {
|
||||
let available_models = match self.models_manager.try_list_models(&self.config) {
|
||||
Ok(models) => models,
|
||||
Err(_) => return,
|
||||
};
|
||||
|
||||
model_migration::maybe_schedule_model_migration_notice(
|
||||
&self.config,
|
||||
model,
|
||||
&available_models,
|
||||
);
|
||||
model_migration::refresh_pending_model_migration_notice(&self.config, &available_models);
|
||||
}
|
||||
|
||||
/// Handle a turn aborted due to user interrupt (Esc).
|
||||
|
||||
@@ -15,6 +15,9 @@ pub(crate) struct PendingModelMigrationNotice {
|
||||
// Used to respect hide flags even if config changes between scheduling and display.
|
||||
#[serde(default)]
|
||||
pub(crate) migration_config_key: Option<String>,
|
||||
/// Unix timestamp (seconds) when this notice was scheduled. Used to expire stale notices.
|
||||
#[serde(default)]
|
||||
pub(crate) scheduled_at_unix_seconds: Option<u64>,
|
||||
}
|
||||
|
||||
/// Read and clear the one-shot migration notice file, returning the notice if it should be shown.
|
||||
@@ -50,6 +53,11 @@ pub(crate) fn take_pending_model_migration_notice(
|
||||
}
|
||||
};
|
||||
|
||||
if notice_expired(¬ice) {
|
||||
let _ = std::fs::remove_file(¬ice_path);
|
||||
return None;
|
||||
}
|
||||
|
||||
if let Some(migration_config_key) = notice.migration_config_key.as_deref()
|
||||
&& migration_prompt_hidden(config, migration_config_key)
|
||||
{
|
||||
@@ -75,11 +83,30 @@ pub(crate) fn take_pending_model_migration_notice(
|
||||
Some(notice)
|
||||
}
|
||||
|
||||
pub(crate) fn maybe_schedule_model_migration_notice(
|
||||
/// Persist the migration notice for the next startup, replacing any existing scheduled notice.
|
||||
///
|
||||
/// Scheduling is intentionally independent of session configuration: it uses the user's config
|
||||
/// (or the default model preset) to determine what to schedule.
|
||||
pub(crate) fn refresh_pending_model_migration_notice(
|
||||
config: &Config,
|
||||
current_model: &str,
|
||||
available_models: &[ModelPreset],
|
||||
) {
|
||||
let current_model = config
|
||||
.model
|
||||
.as_deref()
|
||||
.filter(|model| !model.is_empty())
|
||||
.or_else(|| {
|
||||
available_models
|
||||
.iter()
|
||||
.find(|preset| preset.is_default)
|
||||
.map(|preset| preset.model.as_str())
|
||||
});
|
||||
|
||||
let Some(current_model) = current_model else {
|
||||
clear_pending_model_migration_notice(config);
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(ModelUpgrade {
|
||||
id: target_model,
|
||||
migration_config_key,
|
||||
@@ -89,10 +116,12 @@ pub(crate) fn maybe_schedule_model_migration_notice(
|
||||
.find(|preset| preset.model == current_model)
|
||||
.and_then(|preset| preset.upgrade.as_ref())
|
||||
else {
|
||||
clear_pending_model_migration_notice(config);
|
||||
return;
|
||||
};
|
||||
|
||||
if migration_prompt_hidden(config, migration_config_key.as_str()) {
|
||||
clear_pending_model_migration_notice(config);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -100,6 +129,7 @@ pub(crate) fn maybe_schedule_model_migration_notice(
|
||||
.iter()
|
||||
.all(|preset| preset.model != target_model.as_str())
|
||||
{
|
||||
clear_pending_model_migration_notice(config);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -109,18 +139,17 @@ pub(crate) fn maybe_schedule_model_migration_notice(
|
||||
available_models,
|
||||
config,
|
||||
) {
|
||||
clear_pending_model_migration_notice(config);
|
||||
return;
|
||||
}
|
||||
|
||||
let notice_path = pending_model_migration_notice_path(config);
|
||||
if notice_path.exists() {
|
||||
return;
|
||||
}
|
||||
|
||||
let notice = PendingModelMigrationNotice {
|
||||
from_model: current_model.to_string(),
|
||||
to_model: target_model.to_string(),
|
||||
migration_config_key: Some(migration_config_key.to_string()),
|
||||
scheduled_at_unix_seconds: now_unix_seconds(),
|
||||
};
|
||||
let Ok(json_line) = serde_json::to_string(¬ice).map(|json| format!("{json}\n")) else {
|
||||
return;
|
||||
@@ -167,6 +196,29 @@ fn migration_prompt_hidden(config: &Config, migration_config_key: &str) -> bool
|
||||
}
|
||||
}
|
||||
|
||||
fn clear_pending_model_migration_notice(config: &Config) {
|
||||
let _ = std::fs::remove_file(pending_model_migration_notice_path(config));
|
||||
}
|
||||
|
||||
fn now_unix_seconds() -> Option<u64> {
|
||||
std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.ok()
|
||||
.map(|d| d.as_secs())
|
||||
}
|
||||
|
||||
fn notice_expired(notice: &PendingModelMigrationNotice) -> bool {
|
||||
let Some(scheduled_at) = notice.scheduled_at_unix_seconds else {
|
||||
return false;
|
||||
};
|
||||
let Some(now) = now_unix_seconds() else {
|
||||
return false;
|
||||
};
|
||||
|
||||
const WEEK_SECONDS: u64 = 7 * 24 * 60 * 60;
|
||||
now.saturating_sub(scheduled_at) > WEEK_SECONDS
|
||||
}
|
||||
|
||||
fn should_show_model_migration_notice(
|
||||
current_model: &str,
|
||||
target_model: &str,
|
||||
|
||||
@@ -279,6 +279,11 @@ impl App {
|
||||
}
|
||||
};
|
||||
|
||||
// Startup-only: show any previously scheduled model migration notice, then refresh the
|
||||
// schedule to reflect the current config + model presets (at most one scheduled notice).
|
||||
chat_widget.maybe_show_pending_model_migration_notice();
|
||||
chat_widget.refresh_pending_model_migration_notice();
|
||||
|
||||
chat_widget.maybe_prompt_windows_sandbox_enable();
|
||||
|
||||
let file_search = FileSearchManager::new(config.cwd.clone(), app_event_tx.clone());
|
||||
|
||||
@@ -418,7 +418,6 @@ impl ChatWidget {
|
||||
event,
|
||||
self.show_welcome_banner,
|
||||
));
|
||||
self.maybe_show_pending_model_migration_notice();
|
||||
if let Some(messages) = initial_messages {
|
||||
self.replay_initial_messages(messages);
|
||||
}
|
||||
@@ -435,7 +434,6 @@ impl ChatWidget {
|
||||
// turn now that we know the session is configured.
|
||||
self.maybe_send_next_queued_input();
|
||||
}
|
||||
self.maybe_schedule_model_migration_notice(&model_for_header);
|
||||
if !self.suppress_session_configured_redraw {
|
||||
self.request_redraw();
|
||||
}
|
||||
@@ -750,7 +748,7 @@ impl ChatWidget {
|
||||
self.request_redraw();
|
||||
}
|
||||
|
||||
fn maybe_show_pending_model_migration_notice(&mut self) {
|
||||
pub(crate) fn maybe_show_pending_model_migration_notice(&mut self) {
|
||||
let Some(notice) = model_migration::take_pending_model_migration_notice(&mut self.config)
|
||||
else {
|
||||
return;
|
||||
@@ -770,17 +768,13 @@ impl ChatWidget {
|
||||
});
|
||||
}
|
||||
|
||||
fn maybe_schedule_model_migration_notice(&self, model: &str) {
|
||||
pub(crate) fn refresh_pending_model_migration_notice(&self) {
|
||||
let available_models = match self.models_manager.try_list_models(&self.config) {
|
||||
Ok(models) => models,
|
||||
Err(_) => return,
|
||||
};
|
||||
|
||||
model_migration::maybe_schedule_model_migration_notice(
|
||||
&self.config,
|
||||
model,
|
||||
&available_models,
|
||||
);
|
||||
model_migration::refresh_pending_model_migration_notice(&self.config, &available_models);
|
||||
}
|
||||
|
||||
fn on_mcp_startup_complete(&mut self, ev: McpStartupCompleteEvent) {
|
||||
|
||||
@@ -15,6 +15,9 @@ pub(crate) struct PendingModelMigrationNotice {
|
||||
// Used to respect hide flags even if config changes between scheduling and display.
|
||||
#[serde(default)]
|
||||
pub(crate) migration_config_key: Option<String>,
|
||||
/// Unix timestamp (seconds) when this notice was scheduled. Used to expire stale notices.
|
||||
#[serde(default)]
|
||||
pub(crate) scheduled_at_unix_seconds: Option<u64>,
|
||||
}
|
||||
|
||||
/// Read and clear the one-shot migration notice file, returning the notice if it should be shown.
|
||||
@@ -50,6 +53,11 @@ pub(crate) fn take_pending_model_migration_notice(
|
||||
}
|
||||
};
|
||||
|
||||
if notice_expired(¬ice) {
|
||||
let _ = std::fs::remove_file(¬ice_path);
|
||||
return None;
|
||||
}
|
||||
|
||||
if let Some(migration_config_key) = notice.migration_config_key.as_deref()
|
||||
&& migration_prompt_hidden(config, migration_config_key)
|
||||
{
|
||||
@@ -75,11 +83,30 @@ pub(crate) fn take_pending_model_migration_notice(
|
||||
Some(notice)
|
||||
}
|
||||
|
||||
pub(crate) fn maybe_schedule_model_migration_notice(
|
||||
/// Persist the migration notice for the next startup, replacing any existing scheduled notice.
|
||||
///
|
||||
/// Scheduling is intentionally independent of session configuration: it uses the user's config
|
||||
/// (or the default model preset) to determine what to schedule.
|
||||
pub(crate) fn refresh_pending_model_migration_notice(
|
||||
config: &Config,
|
||||
current_model: &str,
|
||||
available_models: &[ModelPreset],
|
||||
) {
|
||||
let current_model = config
|
||||
.model
|
||||
.as_deref()
|
||||
.filter(|model| !model.is_empty())
|
||||
.or_else(|| {
|
||||
available_models
|
||||
.iter()
|
||||
.find(|preset| preset.is_default)
|
||||
.map(|preset| preset.model.as_str())
|
||||
});
|
||||
|
||||
let Some(current_model) = current_model else {
|
||||
clear_pending_model_migration_notice(config);
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(ModelUpgrade {
|
||||
id: target_model,
|
||||
migration_config_key,
|
||||
@@ -89,10 +116,12 @@ pub(crate) fn maybe_schedule_model_migration_notice(
|
||||
.find(|preset| preset.model == current_model)
|
||||
.and_then(|preset| preset.upgrade.as_ref())
|
||||
else {
|
||||
clear_pending_model_migration_notice(config);
|
||||
return;
|
||||
};
|
||||
|
||||
if migration_prompt_hidden(config, migration_config_key.as_str()) {
|
||||
clear_pending_model_migration_notice(config);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -100,6 +129,7 @@ pub(crate) fn maybe_schedule_model_migration_notice(
|
||||
.iter()
|
||||
.all(|preset| preset.model != target_model.as_str())
|
||||
{
|
||||
clear_pending_model_migration_notice(config);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -109,18 +139,17 @@ pub(crate) fn maybe_schedule_model_migration_notice(
|
||||
available_models,
|
||||
config,
|
||||
) {
|
||||
clear_pending_model_migration_notice(config);
|
||||
return;
|
||||
}
|
||||
|
||||
let notice_path = pending_model_migration_notice_path(config);
|
||||
if notice_path.exists() {
|
||||
return;
|
||||
}
|
||||
|
||||
let notice = PendingModelMigrationNotice {
|
||||
from_model: current_model.to_string(),
|
||||
to_model: target_model.to_string(),
|
||||
migration_config_key: Some(migration_config_key.to_string()),
|
||||
scheduled_at_unix_seconds: now_unix_seconds(),
|
||||
};
|
||||
let Ok(json_line) = serde_json::to_string(¬ice).map(|json| format!("{json}\n")) else {
|
||||
return;
|
||||
@@ -167,6 +196,29 @@ fn migration_prompt_hidden(config: &Config, migration_config_key: &str) -> bool
|
||||
}
|
||||
}
|
||||
|
||||
fn clear_pending_model_migration_notice(config: &Config) {
|
||||
let _ = std::fs::remove_file(pending_model_migration_notice_path(config));
|
||||
}
|
||||
|
||||
fn now_unix_seconds() -> Option<u64> {
|
||||
std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.ok()
|
||||
.map(|d| d.as_secs())
|
||||
}
|
||||
|
||||
fn notice_expired(notice: &PendingModelMigrationNotice) -> bool {
|
||||
let Some(scheduled_at) = notice.scheduled_at_unix_seconds else {
|
||||
return false;
|
||||
};
|
||||
let Some(now) = now_unix_seconds() else {
|
||||
return false;
|
||||
};
|
||||
|
||||
const WEEK_SECONDS: u64 = 7 * 24 * 60 * 60;
|
||||
now.saturating_sub(scheduled_at) > WEEK_SECONDS
|
||||
}
|
||||
|
||||
fn should_show_model_migration_notice(
|
||||
current_model: &str,
|
||||
target_model: &str,
|
||||
|
||||
Reference in New Issue
Block a user