mirror of
https://github.com/openai/codex.git
synced 2026-10-31 19:28:49 +00:00
Use availability NUX messages in tui
This commit is contained in:
@@ -39,8 +39,8 @@ pub enum ConfigEdit {
|
||||
SetNoticeHideModelMigrationPrompt(String, bool),
|
||||
/// Record that a migration prompt was shown for an old->new model mapping.
|
||||
RecordModelMigrationSeen { from: String, to: String },
|
||||
/// Record that an availability NUX was displayed.
|
||||
RecordAvailabilityNuxDisplay { id: String },
|
||||
/// Record that an availability NUX message was displayed.
|
||||
RecordAvailabilityNuxDisplay { message: String },
|
||||
/// Replace the entire `[mcp_servers]` table.
|
||||
ReplaceMcpServers(BTreeMap<String, McpServerConfig>),
|
||||
/// Set or clear a skill config entry under `[[skills.config]]`.
|
||||
@@ -333,7 +333,7 @@ impl ConfigDocument {
|
||||
&[Notice::TABLE_KEY, "model_migrations", from.as_str()],
|
||||
value(to.clone()),
|
||||
)),
|
||||
ConfigEdit::RecordAvailabilityNuxDisplay { id } => {
|
||||
ConfigEdit::RecordAvailabilityNuxDisplay { message } => {
|
||||
let resolved = self.scoped_segments(
|
||||
Scope::Global,
|
||||
&[Notice::TABLE_KEY, "availability_nux_display_counts"],
|
||||
@@ -342,10 +342,10 @@ impl ConfigDocument {
|
||||
return Ok(false);
|
||||
};
|
||||
let current = table
|
||||
.get(id.as_str())
|
||||
.get(message.as_str())
|
||||
.and_then(toml_edit::Item::as_integer)
|
||||
.unwrap_or(0);
|
||||
table[id.as_str()] = value(current + 1);
|
||||
table[message.as_str()] = value(current + 1);
|
||||
Ok(true)
|
||||
}
|
||||
ConfigEdit::SetWindowsWslSetupAcknowledged(acknowledged) => Ok(self.write_value(
|
||||
@@ -805,9 +805,10 @@ impl ConfigEditsBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn record_availability_nux_display(mut self, id: &str) -> Self {
|
||||
self.edits
|
||||
.push(ConfigEdit::RecordAvailabilityNuxDisplay { id: id.to_string() });
|
||||
pub fn record_availability_nux_display(mut self, message: &str) -> Self {
|
||||
self.edits.push(ConfigEdit::RecordAvailabilityNuxDisplay {
|
||||
message: message.to_string(),
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
@@ -1467,7 +1468,7 @@ gpt-5 = "gpt-5.1"
|
||||
existing = "value"
|
||||
|
||||
[notice.availability_nux_display_counts]
|
||||
try_spark = 1
|
||||
"*New* Spark is now available to you." = 1
|
||||
"#,
|
||||
)
|
||||
.expect("seed");
|
||||
@@ -1475,7 +1476,7 @@ try_spark = 1
|
||||
codex_home,
|
||||
None,
|
||||
&[ConfigEdit::RecordAvailabilityNuxDisplay {
|
||||
id: "try_spark".to_string(),
|
||||
message: "*New* Spark is now available to you.".to_string(),
|
||||
}],
|
||||
)
|
||||
.expect("persist");
|
||||
@@ -1486,7 +1487,7 @@ try_spark = 1
|
||||
existing = "value"
|
||||
|
||||
[notice.availability_nux_display_counts]
|
||||
try_spark = 2
|
||||
"*New* Spark is now available to you." = 2
|
||||
"#;
|
||||
assert_eq!(contents, expected);
|
||||
}
|
||||
|
||||
@@ -2743,16 +2743,16 @@ impl App {
|
||||
));
|
||||
}
|
||||
}
|
||||
AppEvent::PersistAvailabilityNuxDisplayed { id } => {
|
||||
AppEvent::PersistAvailabilityNuxDisplayed { message } => {
|
||||
let count = self
|
||||
.config
|
||||
.notices
|
||||
.availability_nux_display_counts
|
||||
.entry(id.clone())
|
||||
.entry(message.clone())
|
||||
.or_default();
|
||||
*count += 1;
|
||||
if let Err(err) = ConfigEditsBuilder::new(&self.config.codex_home)
|
||||
.record_availability_nux_display(id.as_str())
|
||||
.record_availability_nux_display(message.as_str())
|
||||
.apply()
|
||||
.await
|
||||
{
|
||||
|
||||
@@ -337,7 +337,7 @@ pub(crate) enum AppEvent {
|
||||
|
||||
/// Persist that an availability NUX was displayed.
|
||||
PersistAvailabilityNuxDisplayed {
|
||||
id: String,
|
||||
message: String,
|
||||
},
|
||||
|
||||
/// Skip the next world-writable scan (one-shot) after a user-confirmed continue.
|
||||
|
||||
@@ -1179,31 +1179,31 @@ impl ChatWidget {
|
||||
displayed_startup_tips,
|
||||
);
|
||||
self.apply_session_info_cell(session_info_cell);
|
||||
let displayed_availability_nux_ids: Vec<String> = if self.show_welcome_banner {
|
||||
let displayed_availability_nux_messages: Vec<String> = if self.show_welcome_banner {
|
||||
startup_tips
|
||||
.first_session_tips
|
||||
.iter()
|
||||
.filter_map(StartupTip::availability_nux_id)
|
||||
.filter_map(StartupTip::availability_nux_message)
|
||||
.map(str::to_string)
|
||||
.collect()
|
||||
} else {
|
||||
startup_tips
|
||||
.selected_tip
|
||||
.iter()
|
||||
.filter_map(StartupTip::availability_nux_id)
|
||||
.filter_map(StartupTip::availability_nux_message)
|
||||
.map(str::to_string)
|
||||
.collect()
|
||||
};
|
||||
for id in displayed_availability_nux_ids {
|
||||
for message in displayed_availability_nux_messages {
|
||||
let count = self
|
||||
.config
|
||||
.notices
|
||||
.availability_nux_display_counts
|
||||
.entry(id.clone())
|
||||
.entry(message.clone())
|
||||
.or_default();
|
||||
*count += 1;
|
||||
self.app_event_tx
|
||||
.send(AppEvent::PersistAvailabilityNuxDisplayed { id });
|
||||
.send(AppEvent::PersistAvailabilityNuxDisplayed { message });
|
||||
}
|
||||
|
||||
if let Some(messages) = initial_messages {
|
||||
|
||||
@@ -1849,7 +1849,6 @@ async fn session_configured_first_session_shows_model_nux_and_increments_display
|
||||
model_info.display_name = "GPT Test".to_string();
|
||||
model_info.description = Some("Fast, high-reliability coding model.".to_string());
|
||||
model_info.availability_nux = Some(ModelAvailabilityNux {
|
||||
id: "try_spark".to_string(),
|
||||
message:
|
||||
"*New* You're using **GPT Test**. Fast, high-reliability coding model. Use /model to compare or switch anytime."
|
||||
.to_string(),
|
||||
@@ -1893,7 +1892,9 @@ async fn session_configured_first_session_shows_model_nux_and_increments_display
|
||||
chat.config
|
||||
.notices
|
||||
.availability_nux_display_counts
|
||||
.get("try_spark")
|
||||
.get(
|
||||
"*New* You're using **GPT Test**. Fast, high-reliability coding model. Use /model to compare or switch anytime.",
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1937,7 +1938,7 @@ async fn session_configured_ineligible_model_does_not_increment_display_count()
|
||||
chat.config
|
||||
.notices
|
||||
.availability_nux_display_counts
|
||||
.get("try_spark")
|
||||
.get("*New* Spark is now available to you.")
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -53,20 +53,20 @@ fn experimental_tooltips() -> Vec<&'static str> {
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub(crate) enum StartupTip {
|
||||
Generic(String),
|
||||
AvailabilityNux { id: String, message: String },
|
||||
AvailabilityNux { message: String },
|
||||
}
|
||||
|
||||
impl StartupTip {
|
||||
pub(crate) fn message(&self) -> String {
|
||||
match self {
|
||||
Self::Generic(message) | Self::AvailabilityNux { message, .. } => message.clone(),
|
||||
Self::Generic(message) | Self::AvailabilityNux { message } => message.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn availability_nux_id(&self) -> Option<&str> {
|
||||
pub(crate) fn availability_nux_message(&self) -> Option<&str> {
|
||||
match self {
|
||||
Self::Generic(_) => None,
|
||||
Self::AvailabilityNux { id, .. } => Some(id.as_str()),
|
||||
Self::AvailabilityNux { message } => Some(message.as_str()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -188,26 +188,25 @@ fn availability_nux_tips(
|
||||
models: &[ModelPreset],
|
||||
availability_nux_display_counts: &BTreeMap<String, u32>,
|
||||
) -> Vec<StartupTip> {
|
||||
let mut seen_ids = HashSet::new();
|
||||
let mut seen_messages = HashSet::new();
|
||||
|
||||
models
|
||||
.iter()
|
||||
.filter_map(|preset| preset.availability_nux.as_ref())
|
||||
.filter(|availability_nux| {
|
||||
availability_nux_display_counts
|
||||
.get(&availability_nux.id)
|
||||
.get(&availability_nux.message)
|
||||
.copied()
|
||||
.unwrap_or(0)
|
||||
< 4
|
||||
})
|
||||
.filter(|availability_nux| seen_ids.insert(availability_nux.id.clone()))
|
||||
.filter(|availability_nux| seen_messages.insert(availability_nux.message.clone()))
|
||||
.map(startup_tip_from_availability_nux)
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn startup_tip_from_availability_nux(availability_nux: &ModelAvailabilityNux) -> StartupTip {
|
||||
StartupTip::AvailabilityNux {
|
||||
id: availability_nux.id.clone(),
|
||||
message: availability_nux.message.clone(),
|
||||
}
|
||||
}
|
||||
@@ -373,7 +372,7 @@ mod tests {
|
||||
use rand::SeedableRng;
|
||||
use rand::rngs::StdRng;
|
||||
|
||||
fn model_preset(availability_nux: Option<(&str, &str)>, description: &str) -> ModelPreset {
|
||||
fn model_preset(availability_nux: Option<&str>, description: &str) -> ModelPreset {
|
||||
ModelPreset {
|
||||
id: "gpt-test".to_string(),
|
||||
model: "gpt-test".to_string(),
|
||||
@@ -385,8 +384,7 @@ mod tests {
|
||||
is_default: false,
|
||||
upgrade: None,
|
||||
show_in_picker: true,
|
||||
availability_nux: availability_nux.map(|(id, message)| ModelAvailabilityNux {
|
||||
id: id.to_string(),
|
||||
availability_nux: availability_nux.map(|message| ModelAvailabilityNux {
|
||||
message: message.to_string(),
|
||||
}),
|
||||
supported_in_api: true,
|
||||
@@ -514,7 +512,7 @@ content = "This is a test announcement"
|
||||
#[test]
|
||||
fn first_session_eligible_model_returns_all_availability_nux_tips() {
|
||||
let preset = model_preset(
|
||||
Some(("try_spark", "*New* Spark is now available to you.")),
|
||||
Some("*New* Spark is now available to you."),
|
||||
"Fast, high-reliability coding model.",
|
||||
);
|
||||
let mut rng = StdRng::seed_from_u64(1);
|
||||
@@ -531,7 +529,6 @@ content = "This is a test announcement"
|
||||
assert_eq!(
|
||||
StartupTips {
|
||||
first_session_tips: vec![StartupTip::AvailabilityNux {
|
||||
id: "try_spark".to_string(),
|
||||
message: "*New* Spark is now available to you.".to_string(),
|
||||
}],
|
||||
selected_tip: None,
|
||||
@@ -558,21 +555,14 @@ content = "This is a test announcement"
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn first_session_dedupes_multiple_availability_nuxes_by_id() {
|
||||
fn first_session_dedupes_multiple_availability_nuxes_by_message() {
|
||||
let mut rng = StdRng::seed_from_u64(1);
|
||||
let models = vec![
|
||||
model_preset(
|
||||
Some(("try_spark", "*New* Spark is now available to you.")),
|
||||
"",
|
||||
),
|
||||
model_preset(
|
||||
Some(("try_spark", "*New* Spark is now available to you.")),
|
||||
"",
|
||||
),
|
||||
model_preset(Some("*New* Spark is now available to you."), ""),
|
||||
model_preset(Some("*New* Spark is now available to you."), ""),
|
||||
ModelPreset {
|
||||
model: "gpt-other".to_string(),
|
||||
availability_nux: Some(ModelAvailabilityNux {
|
||||
id: "try_canvas".to_string(),
|
||||
message: "*New* Canvas is now available to you.".to_string(),
|
||||
}),
|
||||
..model_preset(None, "")
|
||||
@@ -592,11 +582,9 @@ content = "This is a test announcement"
|
||||
StartupTips {
|
||||
first_session_tips: vec![
|
||||
StartupTip::AvailabilityNux {
|
||||
id: "try_spark".to_string(),
|
||||
message: "*New* Spark is now available to you.".to_string(),
|
||||
},
|
||||
StartupTip::AvailabilityNux {
|
||||
id: "try_canvas".to_string(),
|
||||
message: "*New* Canvas is now available to you.".to_string(),
|
||||
},
|
||||
],
|
||||
@@ -608,10 +596,7 @@ content = "This is a test announcement"
|
||||
|
||||
#[test]
|
||||
fn later_session_can_select_availability_nux_from_weighted_pool() {
|
||||
let preset = model_preset(
|
||||
Some(("try_spark", "*New* Spark is now available to you.")),
|
||||
"",
|
||||
);
|
||||
let preset = model_preset(Some("*New* Spark is now available to you."), "");
|
||||
let mut rng = StdRng::seed_from_u64(5);
|
||||
|
||||
let tips = get_startup_tips_with_rng(
|
||||
@@ -627,7 +612,6 @@ content = "This is a test announcement"
|
||||
StartupTips {
|
||||
first_session_tips: Vec::new(),
|
||||
selected_tip: Some(StartupTip::AvailabilityNux {
|
||||
id: "try_spark".to_string(),
|
||||
message: "*New* Spark is now available to you.".to_string(),
|
||||
}),
|
||||
},
|
||||
@@ -637,11 +621,8 @@ content = "This is a test announcement"
|
||||
|
||||
#[test]
|
||||
fn later_session_count_limit_disables_availability_nux() {
|
||||
let preset = model_preset(
|
||||
Some(("try_spark", "*New* Spark is now available to you.")),
|
||||
"",
|
||||
);
|
||||
let counts = BTreeMap::from([("try_spark".to_string(), 4)]);
|
||||
let preset = model_preset(Some("*New* Spark is now available to you."), "");
|
||||
let counts = BTreeMap::from([("*New* Spark is now available to you.".to_string(), 4)]);
|
||||
let mut rng = StdRng::seed_from_u64(5);
|
||||
|
||||
let tips = get_startup_tips_with_rng(
|
||||
@@ -664,10 +645,7 @@ content = "This is a test announcement"
|
||||
|
||||
#[test]
|
||||
fn later_session_eligible_model_includes_announcement_and_generic_candidates() {
|
||||
let preset = model_preset(
|
||||
Some(("try_spark", "*New* Spark is now available to you.")),
|
||||
"",
|
||||
);
|
||||
let preset = model_preset(Some("*New* Spark is now available to you."), "");
|
||||
let mut saw_announcement = false;
|
||||
let mut saw_plan_tip = false;
|
||||
let mut saw_random_tip = false;
|
||||
|
||||
Reference in New Issue
Block a user