This commit is contained in:
Ahmed Ibrahim
2025-11-18 11:19:38 -08:00
parent 093cd9cf04
commit c025309d32
8 changed files with 180 additions and 196 deletions

View File

@@ -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,
@@ -27,7 +27,7 @@ fn model_from_preset(preset: ModelPreset) -> Model {
}
fn reasoning_efforts_from_preset(
efforts: &'static [ReasoningEffortPreset],
efforts: &[ReasoningEffortPreset],
) -> Vec<ReasoningEffortOption> {
efforts
.iter()

View File

@@ -11,6 +11,38 @@ pub struct ReasoningEffortPreset {
pub effort: ReasoningEffort,
/// Short human description shown next to the effort in UIs.
pub description: &'static str,
/// Optional friendly label shown in featured pickers.
pub label: Option<&'static str>,
}
impl ReasoningEffortPreset {
pub const fn new(
effort: ReasoningEffort,
description: &'static str,
label: Option<&'static str>,
) -> Self {
Self {
effort,
description,
label,
}
}
pub const fn with_label(
effort: ReasoningEffort,
description: &'static str,
label: &'static str,
) -> Self {
Self {
effort,
description,
label: Some(label),
}
}
pub fn label(&self) -> &'static str {
self.label.unwrap_or_else(|| self.effort.label())
}
}
#[derive(Debug, Clone)]
@@ -33,7 +65,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: &'static [ReasoningEffortPreset],
pub supported_reasoning_efforts: Vec<ReasoningEffortPreset>,
/// Whether this is the default model for new users.
pub is_default: bool,
/// recommended upgrade model
@@ -48,19 +80,18 @@ static PRESETS: Lazy<Vec<ModelPreset>> = 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: &[
ReasoningEffortPreset {
effort: ReasoningEffort::Low,
description: "Works faster",
},
ReasoningEffortPreset {
effort: ReasoningEffort::Medium,
description: "Balances speed with intelligence",
},
ReasoningEffortPreset {
effort: ReasoningEffort::High,
description: "Works longer for harder tasks",
},
supported_reasoning_efforts: vec![
ReasoningEffortPreset::with_label(ReasoningEffort::Low, "Works faster", "Fast"),
ReasoningEffortPreset::with_label(
ReasoningEffort::Medium,
"Balances speed with intelligence",
"Balanced",
),
ReasoningEffortPreset::with_label(
ReasoningEffort::High,
"Works longer for harder tasks",
"Thorough",
),
],
is_default: true,
upgrade: None,
@@ -71,19 +102,22 @@ static PRESETS: Lazy<Vec<ModelPreset>> = Lazy::new(|| {
display_name: "gpt-5.1-codex",
description: "Optimized for codex.",
default_reasoning_effort: ReasoningEffort::Medium,
supported_reasoning_efforts: &[
ReasoningEffortPreset {
effort: ReasoningEffort::Low,
description: "Fastest responses with limited reasoning",
},
ReasoningEffortPreset {
effort: ReasoningEffort::Medium,
description: "Dynamically adjusts reasoning based on the task",
},
ReasoningEffortPreset {
effort: ReasoningEffort::High,
description: "Maximizes reasoning depth for complex or ambiguous problems",
},
supported_reasoning_efforts: vec![
ReasoningEffortPreset::new(
ReasoningEffort::Low,
"Fastest responses with limited reasoning",
None,
),
ReasoningEffortPreset::new(
ReasoningEffort::Medium,
"Dynamically adjusts reasoning based on the task",
None,
),
ReasoningEffortPreset::new(
ReasoningEffort::High,
"Maximizes reasoning depth for complex or ambiguous problems",
None,
),
],
is_default: false,
upgrade: None,
@@ -94,15 +128,17 @@ static PRESETS: Lazy<Vec<ModelPreset>> = 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: &[
ReasoningEffortPreset {
effort: ReasoningEffort::Medium,
description: "Dynamically adjusts reasoning based on the task",
},
ReasoningEffortPreset {
effort: ReasoningEffort::High,
description: "Maximizes reasoning depth for complex or ambiguous problems",
},
supported_reasoning_efforts: vec![
ReasoningEffortPreset::new(
ReasoningEffort::Medium,
"Dynamically adjusts reasoning based on the task",
None,
),
ReasoningEffortPreset::new(
ReasoningEffort::High,
"Maximizes reasoning depth for complex or ambiguous problems",
None,
),
],
is_default: false,
upgrade: None,
@@ -113,19 +149,22 @@ static PRESETS: Lazy<Vec<ModelPreset>> = Lazy::new(|| {
display_name: "gpt-5.1",
description: "Broad world knowledge with strong general reasoning.",
default_reasoning_effort: ReasoningEffort::Medium,
supported_reasoning_efforts: &[
ReasoningEffortPreset {
effort: ReasoningEffort::Low,
description: "Balances speed with some reasoning; useful for straightforward queries and short explanations",
},
ReasoningEffortPreset {
effort: ReasoningEffort::Medium,
description: "Provides a solid balance of reasoning depth and latency for general-purpose tasks",
},
ReasoningEffortPreset {
effort: ReasoningEffort::High,
description: "Maximizes reasoning depth for complex or ambiguous problems",
},
supported_reasoning_efforts: vec![
ReasoningEffortPreset::new(
ReasoningEffort::Low,
"Balances speed with some reasoning; useful for straightforward queries and short explanations",
None,
),
ReasoningEffortPreset::new(
ReasoningEffort::Medium,
"Provides a solid balance of reasoning depth and latency for general-purpose tasks",
None,
),
ReasoningEffortPreset::new(
ReasoningEffort::High,
"Maximizes reasoning depth for complex or ambiguous problems",
None,
),
],
is_default: false,
upgrade: None,
@@ -137,19 +176,22 @@ static PRESETS: Lazy<Vec<ModelPreset>> = Lazy::new(|| {
display_name: "gpt-5-codex",
description: "Optimized for codex.",
default_reasoning_effort: ReasoningEffort::Medium,
supported_reasoning_efforts: &[
ReasoningEffortPreset {
effort: ReasoningEffort::Low,
description: "Fastest responses with limited reasoning",
},
ReasoningEffortPreset {
effort: ReasoningEffort::Medium,
description: "Dynamically adjusts reasoning based on the task",
},
ReasoningEffortPreset {
effort: ReasoningEffort::High,
description: "Maximizes reasoning depth for complex or ambiguous problems",
},
supported_reasoning_efforts: vec![
ReasoningEffortPreset::new(
ReasoningEffort::Low,
"Fastest responses with limited reasoning",
None,
),
ReasoningEffortPreset::new(
ReasoningEffort::Medium,
"Dynamically adjusts reasoning based on the task",
None,
),
ReasoningEffortPreset::new(
ReasoningEffort::High,
"Maximizes reasoning depth for complex or ambiguous problems",
None,
),
],
is_default: false,
upgrade: Some(ModelUpgrade {
@@ -163,15 +205,17 @@ static PRESETS: Lazy<Vec<ModelPreset>> = 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: &[
ReasoningEffortPreset {
effort: ReasoningEffort::Medium,
description: "Dynamically adjusts reasoning based on the task",
},
ReasoningEffortPreset {
effort: ReasoningEffort::High,
description: "Maximizes reasoning depth for complex or ambiguous problems",
},
supported_reasoning_efforts: vec![
ReasoningEffortPreset::new(
ReasoningEffort::Medium,
"Dynamically adjusts reasoning based on the task",
None,
),
ReasoningEffortPreset::new(
ReasoningEffort::High,
"Maximizes reasoning depth for complex or ambiguous problems",
None,
),
],
is_default: false,
upgrade: Some(ModelUpgrade {
@@ -185,23 +229,27 @@ static PRESETS: Lazy<Vec<ModelPreset>> = Lazy::new(|| {
display_name: "gpt-5",
description: "Broad world knowledge with strong general reasoning.",
default_reasoning_effort: ReasoningEffort::Medium,
supported_reasoning_efforts: &[
ReasoningEffortPreset {
effort: ReasoningEffort::Minimal,
description: "Fastest responses with little reasoning",
},
ReasoningEffortPreset {
effort: ReasoningEffort::Low,
description: "Balances speed with some reasoning; useful for straightforward queries and short explanations",
},
ReasoningEffortPreset {
effort: ReasoningEffort::Medium,
description: "Provides a solid balance of reasoning depth and latency for general-purpose tasks",
},
ReasoningEffortPreset {
effort: ReasoningEffort::High,
description: "Maximizes reasoning depth for complex or ambiguous problems",
},
supported_reasoning_efforts: vec![
ReasoningEffortPreset::new(
ReasoningEffort::Minimal,
"Fastest responses with little reasoning",
None,
),
ReasoningEffortPreset::new(
ReasoningEffort::Low,
"Balances speed with some reasoning; useful for straightforward queries and short explanations",
None,
),
ReasoningEffortPreset::new(
ReasoningEffort::Medium,
"Provides a solid balance of reasoning depth and latency for general-purpose tasks",
None,
),
ReasoningEffortPreset::new(
ReasoningEffort::High,
"Maximizes reasoning depth for complex or ambiguous problems",
None,
),
],
is_default: false,
upgrade: Some(ModelUpgrade {
@@ -228,44 +276,15 @@ pub fn all_model_presets() -> &'static Vec<ModelPreset> {
&PRESETS
}
/// Label metadata for featured pickers (e.g., codex-auto variants).
#[derive(Debug, Clone, Copy)]
struct FeaturedEffortLabel {
model: &'static str,
effort: ReasoningEffort,
label: &'static str,
impl ModelPreset {
pub fn reasoning_effort_label(&self, effort: ReasoningEffort) -> &'static str {
self.supported_reasoning_efforts
.iter()
.find(|option| option.effort == effort)
.map(|option| option.label())
.unwrap_or_else(|| effort.label())
}
}
static FEATURED_EFFORT_LABELS: &[FeaturedEffortLabel] = &[
FeaturedEffortLabel {
model: "codex-auto",
effort: ReasoningEffort::Low,
label: "Fast",
},
FeaturedEffortLabel {
model: "codex-auto",
effort: ReasoningEffort::Medium,
label: "Balanced",
},
FeaturedEffortLabel {
model: "codex-auto",
effort: ReasoningEffort::High,
label: "Thorough",
},
];
/// Returns a friendly label for the given model/effort combination when available.
pub fn effort_label_for_model(
model: &str,
effort: Option<ReasoningEffort>,
) -> Option<&'static str> {
let effort = effort?;
FEATURED_EFFORT_LABELS
.iter()
.find(|entry| entry.model == model && entry.effort == effort)
.map(|entry| entry.label)
}
#[cfg(test)]
mod tests {
use super::*;
@@ -275,20 +294,4 @@ mod tests {
let default_models = PRESETS.iter().filter(|preset| preset.is_default).count();
assert!(default_models == 1);
}
#[test]
fn codex_auto_featured_options_define_labels() {
assert_eq!(
effort_label_for_model("codex-auto", Some(ReasoningEffort::Low)),
Some("Fast")
);
assert_eq!(
effort_label_for_model("codex-auto", Some(ReasoningEffort::Medium)),
Some("Balanced")
);
assert_eq!(
effort_label_for_model("codex-auto", Some(ReasoningEffort::High)),
Some("Thorough")
);
}
}

View File

@@ -32,6 +32,19 @@ pub enum ReasoningEffort {
High,
}
impl ReasoningEffort {
/// Friendly label that can be shown in UIs.
pub fn label(self) -> &'static str {
match self {
ReasoningEffort::None => "None",
ReasoningEffort::Minimal => "Minimal",
ReasoningEffort::Low => "Fast",
ReasoningEffort::Medium => "Balanced",
ReasoningEffort::High => "Thorough",
}
}
}
/// A summary of the reasoning performed by the model. This can be useful for
/// debugging and understanding the model's reasoning process.
/// See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#reasoning-summaries

View File

@@ -19,7 +19,6 @@ use crate::update_action::UpdateAction;
use codex_ansi_escape::ansi_escape_line;
use codex_common::model_presets::ModelUpgrade;
use codex_common::model_presets::all_model_presets;
use codex_common::model_presets::effort_label_for_model;
use codex_core::AuthManager;
use codex_core::ConversationManager;
use codex_core::config::Config;
@@ -95,13 +94,10 @@ fn should_show_model_migration_prompt(
}
fn format_model_change_target(model: &str, effort: Option<ReasoningEffortConfig>) -> String {
if let Some(label) = effort_label_for_model(model, effort) {
format!("{model} ({label})")
if let Some(effort) = effort {
format!("{model} ({})", effort.label())
} else {
let suffix = effort
.map(|eff| format!(" with {eff} reasoning"))
.unwrap_or_else(|| " with default reasoning".to_string());
format!("{model}{suffix}")
format!("{model} with default reasoning")
}
}
@@ -1061,10 +1057,10 @@ mod tests {
}
#[test]
fn format_model_change_target_falls_back_to_reasoning_text() {
fn format_model_change_target_uses_effort_label() {
let formatted =
super::format_model_change_target("gpt-5.1-codex", Some(ReasoningEffortConfig::High));
assert_eq!(formatted, "gpt-5.1-codex with high reasoning");
assert_eq!(formatted, "gpt-5.1-codex (Thorough)");
}
#[test]

View File

@@ -122,7 +122,6 @@ use codex_common::approval_presets::ApprovalPreset;
use codex_common::approval_presets::builtin_approval_presets;
use codex_common::model_presets::ModelPreset;
use codex_common::model_presets::builtin_model_presets;
use codex_common::model_presets::effort_label_for_model;
use codex_core::AuthManager;
use codex_core::CodexAuth;
use codex_core::ConversationManager;
@@ -2111,11 +2110,8 @@ impl ChatWidget {
});
})];
let label = effort_label_for_model(&model_slug, Some(effort))
.map(str::to_string)
.unwrap_or_else(|| Self::featured_option_label(effort));
let mut name = effort.label().to_string();
let is_current_option = current_effort == Some(effort);
let mut name = label.to_string();
if effort == default_effort && !is_current_option {
name.push_str(" (default)");
}
@@ -2136,21 +2132,6 @@ impl ChatWidget {
items
}
fn featured_option_label(effort: ReasoningEffortConfig) -> String {
match effort {
ReasoningEffortConfig::Low => "Fast".to_string(),
ReasoningEffortConfig::Medium => "Balanced".to_string(),
ReasoningEffortConfig::High => "Thorough".to_string(),
_ => {
let mut text = effort.to_string();
if let Some(first) = text.get_mut(0..1) {
first.make_ascii_uppercase();
}
text
}
}
}
/// Open a popup to choose the reasoning effort (stage 2) for the given model.
pub(crate) fn open_reasoning_popup(
&mut self,
@@ -2158,7 +2139,7 @@ impl ChatWidget {
preferred_effort: Option<ReasoningEffortConfig>,
) {
let default_effort: ReasoningEffortConfig = preset.default_reasoning_effort;
let supported = preset.supported_reasoning_efforts;
let supported = &preset.supported_reasoning_efforts;
struct EffortChoice {
stored: Option<ReasoningEffortConfig>,
@@ -2211,10 +2192,7 @@ impl ChatWidget {
let mut items: Vec<SelectionItem> = Vec::new();
for choice in choices.iter() {
let effort = choice.display;
let mut effort_label = effort.to_string();
if let Some(first) = effort_label.get_mut(0..1) {
first.make_ascii_uppercase();
}
let mut effort_label = effort.label().to_string();
let is_current_choice = is_current_model && choice.stored == highlight_choice;
if choice.stored == default_choice && !is_current_choice {
effort_label.push_str(" (default)");

View File

@@ -4,11 +4,11 @@ expression: popup
---
Select Reasoning Level for gpt-5.1-codex
1. Low Fastest responses with limited reasoning
2. Medium (default) Dynamically adjusts reasoning based on the task
3. High (current) Maximizes reasoning depth for complex or ambiguous
problems
⚠ High reasoning effort can quickly consume Plus plan
rate limits.
1. Fast Fastest responses with limited reasoning
2. Balanced (default) Dynamically adjusts reasoning based on the task
3. Thorough (current) Maximizes reasoning depth for complex or ambiguous
problems
⚠ High reasoning effort can quickly consume Plus plan
rate limits.
Press enter to confirm or esc to go back

View File

@@ -1573,17 +1573,17 @@ 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();
static SINGLE_EFFORT: [ReasoningEffortPreset; 1] = [ReasoningEffortPreset {
effort: ReasoningEffortConfig::High,
description: "Maximizes reasoning depth for complex or ambiguous problems",
}];
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: &SINGLE_EFFORT,
supported_reasoning_efforts: vec![ReasoningEffortPreset::new(
ReasoningEffortConfig::High,
"Maximizes reasoning depth for complex or ambiguous problems",
None,
)],
is_default: false,
upgrade: None,
};

View File

@@ -707,13 +707,7 @@ impl SessionHeaderHistoryCell {
}
fn reasoning_label(&self) -> Option<&'static str> {
self.reasoning_effort.map(|effort| match effort {
ReasoningEffortConfig::Minimal => "minimal",
ReasoningEffortConfig::Low => "low",
ReasoningEffortConfig::Medium => "medium",
ReasoningEffortConfig::High => "high",
ReasoningEffortConfig::None => "none",
})
self.reasoning_effort.map(|effort| effort.label())
}
}
@@ -1824,7 +1818,7 @@ mod tests {
.find(|line| line.contains("model:"))
.expect("model line");
assert!(model_line.contains("gpt-4o high"));
assert!(model_line.contains("gpt-4o Thorough"));
assert!(model_line.contains("/model to change"));
}