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
+2 -2
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()
+144 -141
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")
);
}
}
+13
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
+5 -9
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]
+3 -25
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)");
@@ -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
+5 -5
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,
};
+2 -8
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"));
}