diff --git a/codex-rs/app-server/src/models.rs b/codex-rs/app-server/src/models.rs index 230816ab64..d0192a3a2f 100644 --- a/codex-rs/app-server/src/models.rs +++ b/codex-rs/app-server/src/models.rs @@ -19,7 +19,7 @@ fn model_from_preset(preset: ModelPreset) -> Model { display_name: preset.display_name.to_string(), description: preset.description.to_string(), supported_reasoning_efforts: reasoning_efforts_from_preset( - &preset.supported_reasoning_efforts, + preset.supported_reasoning_efforts, ), default_reasoning_effort: preset.default_reasoning_effort, is_default: preset.is_default, diff --git a/codex-rs/common/src/model_presets.rs b/codex-rs/common/src/model_presets.rs index eb2fe5e97b..29da9969b2 100644 --- a/codex-rs/common/src/model_presets.rs +++ b/codex-rs/common/src/model_presets.rs @@ -43,7 +43,7 @@ pub struct ModelPreset { /// Reasoning effort applied when none is explicitly chosen. pub default_reasoning_effort: ReasoningEffort, /// Supported reasoning effort options. - pub supported_reasoning_efforts: Vec, + pub supported_reasoning_efforts: &'static [ReasoningEffortPreset], /// Whether this is the default model for new users. pub is_default: bool, /// recommended upgrade model @@ -58,7 +58,7 @@ static PRESETS: Lazy> = Lazy::new(|| { display_name: "codex-auto", description: "Automatically chooses the best Codex model configuration for your task.", default_reasoning_effort: ReasoningEffort::Medium, - supported_reasoning_efforts: vec![ + supported_reasoning_efforts: &[ ReasoningEffortPreset { effort: ReasoningEffort::Low, description: "Works faster", @@ -84,7 +84,7 @@ static PRESETS: Lazy> = Lazy::new(|| { display_name: "gpt-5.1-codex", description: "Optimized for codex.", default_reasoning_effort: ReasoningEffort::Medium, - supported_reasoning_efforts: vec![ + supported_reasoning_efforts: &[ ReasoningEffortPreset { effort: ReasoningEffort::Low, description: "Fastest responses with limited reasoning", @@ -110,7 +110,7 @@ static PRESETS: Lazy> = Lazy::new(|| { display_name: "gpt-5.1-codex-mini", description: "Optimized for codex. Cheaper, faster, but less capable.", default_reasoning_effort: ReasoningEffort::Medium, - supported_reasoning_efforts: vec![ + supported_reasoning_efforts: &[ ReasoningEffortPreset { effort: ReasoningEffort::Medium, description: "Dynamically adjusts reasoning based on the task", @@ -131,7 +131,7 @@ static PRESETS: Lazy> = Lazy::new(|| { display_name: "gpt-5.1", description: "Broad world knowledge with strong general reasoning.", default_reasoning_effort: ReasoningEffort::Medium, - supported_reasoning_efforts: vec![ + supported_reasoning_efforts: &[ ReasoningEffortPreset { effort: ReasoningEffort::Low, description: "Balances speed with some reasoning; useful for straightforward queries and short explanations", @@ -158,7 +158,7 @@ static PRESETS: Lazy> = Lazy::new(|| { display_name: "gpt-5-codex", description: "Optimized for codex.", default_reasoning_effort: ReasoningEffort::Medium, - supported_reasoning_efforts: vec![ + supported_reasoning_efforts: &[ ReasoningEffortPreset { effort: ReasoningEffort::Low, description: "Fastest responses with limited reasoning", @@ -187,7 +187,7 @@ static PRESETS: Lazy> = Lazy::new(|| { display_name: "gpt-5-codex-mini", description: "Optimized for codex. Cheaper, faster, but less capable.", default_reasoning_effort: ReasoningEffort::Medium, - supported_reasoning_efforts: vec![ + supported_reasoning_efforts: &[ ReasoningEffortPreset { effort: ReasoningEffort::Medium, description: "Dynamically adjusts reasoning based on the task", @@ -211,7 +211,7 @@ static PRESETS: Lazy> = Lazy::new(|| { display_name: "gpt-5", description: "Broad world knowledge with strong general reasoning.", default_reasoning_effort: ReasoningEffort::Medium, - supported_reasoning_efforts: vec![ + supported_reasoning_efforts: &[ ReasoningEffortPreset { effort: ReasoningEffort::Minimal, description: "Fastest responses with little reasoning", diff --git a/codex-rs/tui/src/chatwidget.rs b/codex-rs/tui/src/chatwidget.rs index dd828ab691..05c15b4155 100644 --- a/codex-rs/tui/src/chatwidget.rs +++ b/codex-rs/tui/src/chatwidget.rs @@ -2089,49 +2089,6 @@ impl ChatWidget { }); } - fn featured_model_items(&self, preset: &ModelPreset) -> Vec { - let default_effort: ReasoningEffortConfig = preset.default_reasoning_effort; - let is_current = self.config.model == preset.model; - let current_effort = if is_current { - self.config.model_reasoning_effort.or(Some(default_effort)) - } else { - None - }; - - let mut items = Vec::new(); - let model_slug = preset.model.to_string(); - for option in preset.supported_reasoning_efforts.iter() { - let effort = option.effort; - let model_for_action = model_slug.clone(); - let actions: Vec = vec![Box::new(move |tx| { - tx.send(AppEvent::ApplyModelAndEffort { - model: model_for_action.clone(), - effort: Some(effort), - }); - })]; - - let mut name = option.label().to_string(); - let is_current_option = current_effort == Some(effort); - if effort == default_effort && !is_current_option { - name.push_str(" (default)"); - } - - let description = - (!option.description.is_empty()).then(|| option.description.to_string()); - items.push(SelectionItem { - name, - description, - selected_description: None, - is_current: is_current_option, - actions, - dismiss_on_select: true, - ..Default::default() - }); - } - - items - } - /// Open a popup to choose the reasoning effort (stage 2) for the given model. pub(crate) fn open_reasoning_popup( &mut self, @@ -2139,7 +2096,7 @@ impl ChatWidget { preferred_effort: Option, ) { let default_effort: ReasoningEffortConfig = preset.default_reasoning_effort; - let supported = &preset.supported_reasoning_efforts; + let supported = preset.supported_reasoning_efforts; struct EffortChoice { stored: Option, @@ -2271,6 +2228,49 @@ impl ChatWidget { }); } + fn featured_model_items(&self, preset: &ModelPreset) -> Vec { + let default_effort: ReasoningEffortConfig = preset.default_reasoning_effort; + let is_current = self.config.model == preset.model; + let current_effort = if is_current { + self.config.model_reasoning_effort.or(Some(default_effort)) + } else { + None + }; + + let mut items = Vec::new(); + let model_slug = preset.model.to_string(); + for option in preset.supported_reasoning_efforts.iter() { + let effort = option.effort; + let model_for_action = model_slug.clone(); + let actions: Vec = vec![Box::new(move |tx| { + tx.send(AppEvent::ApplyModelAndEffort { + model: model_for_action.clone(), + effort: Some(effort), + }); + })]; + + let mut name = option.label().to_string(); + let is_current_option = current_effort == Some(effort); + if effort == default_effort && !is_current_option { + name.push_str(" (default)"); + } + + let description = + (!option.description.is_empty()).then(|| option.description.to_string()); + items.push(SelectionItem { + name, + description, + selected_description: None, + is_current: is_current_option, + actions, + dismiss_on_select: true, + ..Default::default() + }); + } + + items + } + pub(crate) fn apply_model_and_effort( &self, model: String, diff --git a/codex-rs/tui/src/chatwidget/tests.rs b/codex-rs/tui/src/chatwidget/tests.rs index bf407007dd..b416aa5158 100644 --- a/codex-rs/tui/src/chatwidget/tests.rs +++ b/codex-rs/tui/src/chatwidget/tests.rs @@ -1573,17 +1573,19 @@ fn reasoning_popup_hides_default_label_when_option_is_current() { fn single_reasoning_option_skips_selection() { let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(); + const SINGLE_HIGH_REASONING: &[ReasoningEffortPreset] = &[ReasoningEffortPreset { + effort: ReasoningEffortConfig::High, + description: "Maximizes reasoning depth for complex or ambiguous problems", + label: None, + }]; + let preset = ModelPreset { id: "model-with-single-reasoning", model: "model-with-single-reasoning", display_name: "model-with-single-reasoning", description: "", default_reasoning_effort: ReasoningEffortConfig::High, - supported_reasoning_efforts: vec![ReasoningEffortPreset { - effort: ReasoningEffortConfig::High, - description: "Maximizes reasoning depth for complex or ambiguous problems", - label: None, - }], + supported_reasoning_efforts: SINGLE_HIGH_REASONING, is_default: false, upgrade: None, };