[codex] Support model-defined reasoning efforts (#26444)

## Summary
- accept non-empty model-defined reasoning effort values while
preserving built-in effort behavior
- propagate the non-Copy effort type through core, app-server, TUI,
telemetry, and persistence call sites
- preserve string wire encoding and expose an open-string schema for
clients
- update model selection and shortcut behavior for model-advertised
effort values

## Root cause
`ReasoningEffort` gained a string-backed custom variant, so it could no
longer implement `Copy` or rely on derived closed-enum serialization.
Existing consumers still moved effort values from shared references and
assumed a fixed built-in value set.

## Validation
- `just fmt`
- Local tests and compilation were not run per request; relying on CI.
This commit is contained in:
Ahmed Ibrahim
2026-06-04 13:36:24 -07:00
committed by GitHub
parent 4a70e0ac1b
commit 8ac304c299
97 changed files with 685 additions and 581 deletions
+2 -9
View File
@@ -2503,15 +2503,8 @@
"type": "string"
},
"ReasoningEffort": {
"description": "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning",
"enum": [
"none",
"minimal",
"low",
"medium",
"high",
"xhigh"
],
"description": "A non-empty reasoning effort value advertised by the model.",
"minLength": 1,
"type": "string"
},
"ReasoningSummary": {
+4 -4
View File
@@ -721,7 +721,7 @@ impl ModelClient {
) -> Option<Reasoning> {
if model_info.supports_reasoning_summaries {
Some(Reasoning {
effort: effort.or(model_info.default_reasoning_level),
effort: effort.or_else(|| model_info.default_reasoning_level.clone()),
summary: if summary == ReasoningSummaryConfig::None {
None
} else {
@@ -1267,7 +1267,7 @@ impl ModelClientSession {
&client_setup.api_provider,
prompt,
model_info,
effort,
effort.clone(),
summary,
service_tier.clone(),
)?;
@@ -1376,7 +1376,7 @@ impl ModelClientSession {
&client_setup.api_provider,
prompt,
model_info,
effort,
effort.clone(),
summary,
service_tier.clone(),
)?;
@@ -1595,7 +1595,7 @@ impl ModelClientSession {
prompt,
model_info,
session_telemetry,
effort,
effort.clone(),
summary,
service_tier.clone(),
turn_metadata_header,
+1 -1
View File
@@ -573,7 +573,7 @@ async fn drain_to_completed(
prompt,
&turn_context.model_info,
&turn_context.session_telemetry,
turn_context.reasoning_effort,
turn_context.reasoning_effort.clone(),
turn_context.reasoning_summary,
turn_context.config.service_tier.clone(),
turn_metadata_header,
+1 -1
View File
@@ -242,7 +242,7 @@ async fn run_remote_compact_task_inner_impl(
&prompt,
&turn_context.model_info,
CompactConversationRequestSettings {
effort: turn_context.reasoning_effort,
effort: turn_context.reasoning_effort.clone(),
summary: turn_context.reasoning_summary,
service_tier: if sess.services.auth_manager.auth_mode() == Some(AuthMode::ApiKey) {
None
+1 -1
View File
@@ -339,7 +339,7 @@ async fn run_remote_compaction_request_v2(
prompt,
&turn_context.model_info,
&turn_context.session_telemetry,
turn_context.reasoning_effort,
turn_context.reasoning_effort.clone(),
turn_context.reasoning_summary,
turn_context.config.service_tier.clone(),
turn_metadata_header,
+1 -1
View File
@@ -225,7 +225,7 @@ impl ConfigDocument {
);
mutated |= self.write_optional_value(
&["model_reasoning_effort"],
effort.map(|effort| value(effort.to_string())),
effort.as_ref().map(|effort| value(effort.to_string())),
);
mutated
}),
+4 -3
View File
@@ -702,7 +702,7 @@ pub(super) async fn run_guardian_review_session(
.supported_reasoning_efforts
.iter()
.any(|effort| effort.effort == codex_protocol::openai_models::ReasoningEffort::Low),
Some(preset.default_reasoning_effort),
Some(preset.default_reasoning_effort.clone()),
);
(review_model_id.to_string(), reasoning_effort)
} else {
@@ -712,7 +712,8 @@ pub(super) async fn run_guardian_review_session(
.iter()
.any(|preset| preset.effort == codex_protocol::openai_models::ReasoningEffort::Low),
turn.reasoning_effort
.or(turn.model_info.default_reasoning_level),
.clone()
.or_else(|| turn.model_info.default_reasoning_level.clone()),
);
(
model_override
@@ -725,7 +726,7 @@ pub(super) async fn run_guardian_review_session(
turn.config.as_ref(),
live_network_config.clone(),
guardian_model.as_str(),
guardian_reasoning_effort,
guardian_reasoning_effort.clone(),
);
let guardian_config = match guardian_config {
Ok(config) => config,
+6 -5
View File
@@ -167,7 +167,7 @@ impl GuardianReviewSessionReuseKey {
model_context_window: spawn_config.model_context_window,
model_auto_compact_token_limit: spawn_config.model_auto_compact_token_limit,
model_auto_compact_token_limit_scope: spawn_config.model_auto_compact_token_limit_scope,
model_reasoning_effort: spawn_config.model_reasoning_effort,
model_reasoning_effort: spawn_config.model_reasoning_effort.clone(),
model_reasoning_summary: spawn_config.model_reasoning_summary,
permissions: spawn_config.permissions.clone(),
developer_instructions: spawn_config.developer_instructions.clone(),
@@ -658,7 +658,8 @@ async fn run_review_on_session(
let guardian_reasoning_effort = if model_info.supports_reasoning_summaries {
params
.reasoning_effort
.or(model_info.default_reasoning_level)
.clone()
.or_else(|| model_info.default_reasoning_level.clone())
} else {
None
};
@@ -742,7 +743,7 @@ async fn run_review_on_session(
mode: codex_protocol::config_types::ModeKind::Default,
settings: codex_protocol::config_types::Settings {
model: params.model.clone(),
reasoning_effort: params.reasoning_effort,
reasoning_effort: params.reasoning_effort.clone(),
developer_instructions: None,
},
}),
@@ -1107,7 +1108,7 @@ mod tests {
async fn test_review_params() -> GuardianReviewSessionParams {
let (session, turn) = crate::session::tests::make_session_and_context().await;
let model = turn.model_info.slug.clone();
let reasoning_effort = turn.reasoning_effort;
let reasoning_effort = turn.reasoning_effort.clone();
let reasoning_summary = turn.reasoning_summary;
let personality = turn.personality;
#[allow(deprecated)]
@@ -1116,7 +1117,7 @@ mod tests {
turn.config.as_ref(),
/*live_network_config*/ None,
model.as_str(),
reasoning_effort,
reasoning_effort.clone(),
)
.expect("guardian config");
+1 -1
View File
@@ -129,7 +129,7 @@ fn save_config_resolved_fields(
) -> anyhow::Result<()> {
lock_config.web_search = Some(config.web_search_mode.value());
lock_config.model_provider = Some(config.model_provider_id.clone());
lock_config.plan_mode_reasoning_effort = config.plan_mode_reasoning_effort;
lock_config.plan_mode_reasoning_effort = config.plan_mode_reasoning_effort.clone();
lock_config.model_verbosity = config.model_verbosity;
lock_config.include_permissions_instructions = Some(config.include_permissions_instructions);
lock_config.include_apps_instructions = Some(config.include_apps_instructions);
+1 -1
View File
@@ -579,7 +579,7 @@ impl Codex {
mode: ModeKind::Default,
settings: Settings {
model: model.clone(),
reasoning_effort: config.model_reasoning_effort,
reasoning_effort: config.model_reasoning_effort.clone(),
developer_instructions: None,
},
};
+1 -1
View File
@@ -74,7 +74,7 @@ pub(super) async fn spawn_review_thread(
let auth_manager_for_context = auth_manager.clone();
let provider_for_context = provider.clone();
let session_telemetry_for_context = session_telemetry.clone();
let reasoning_effort = per_turn_config.model_reasoning_effort;
let reasoning_effort = per_turn_config.model_reasoning_effort.clone();
let reasoning_summary = per_turn_config
.model_reasoning_summary
.unwrap_or(model_info.default_reasoning_summary);
@@ -74,7 +74,7 @@ async fn record_initial_history_resumed_bare_turn_context_does_not_hydrate_previ
collaboration_mode: Some(turn_context.collaboration_mode.clone()),
multi_agent_version: None,
realtime_active: Some(turn_context.realtime_active),
effort: turn_context.reasoning_effort,
effort: turn_context.reasoning_effort.clone(),
summary: codex_protocol::config_types::ReasoningSummary::Auto,
};
let rollout_items = vec![RolloutItem::TurnContext(previous_context_item)];
@@ -113,7 +113,7 @@ async fn record_initial_history_resumed_hydrates_previous_turn_settings_from_lif
collaboration_mode: Some(turn_context.collaboration_mode.clone()),
multi_agent_version: None,
realtime_active: Some(turn_context.realtime_active),
effort: turn_context.reasoning_effort,
effort: turn_context.reasoning_effort.clone(),
summary: codex_protocol::config_types::ReasoningSummary::Auto,
};
let turn_id = previous_context_item
@@ -963,7 +963,7 @@ async fn record_initial_history_resumed_turn_context_after_compaction_reestablis
collaboration_mode: Some(turn_context.collaboration_mode.clone()),
multi_agent_version: None,
realtime_active: Some(turn_context.realtime_active),
effort: turn_context.reasoning_effort,
effort: turn_context.reasoning_effort.clone(),
summary: codex_protocol::config_types::ReasoningSummary::Auto,
};
let previous_turn_id = previous_context_item
@@ -1042,7 +1042,7 @@ async fn record_initial_history_resumed_turn_context_after_compaction_reestablis
collaboration_mode: Some(turn_context.collaboration_mode.clone()),
multi_agent_version: None,
realtime_active: Some(turn_context.realtime_active),
effort: turn_context.reasoning_effort,
effort: turn_context.reasoning_effort.clone(),
summary: codex_protocol::config_types::ReasoningSummary::Auto,
}))
.expect("serialize expected reference context item")
@@ -1071,7 +1071,7 @@ async fn record_initial_history_resumed_aborted_turn_without_id_clears_active_tu
collaboration_mode: Some(turn_context.collaboration_mode.clone()),
multi_agent_version: None,
realtime_active: Some(turn_context.realtime_active),
effort: turn_context.reasoning_effort,
effort: turn_context.reasoning_effort.clone(),
summary: codex_protocol::config_types::ReasoningSummary::Auto,
};
let previous_turn_id = previous_context_item
@@ -1190,7 +1190,7 @@ async fn record_initial_history_resumed_unmatched_abort_preserves_active_turn_fo
collaboration_mode: Some(turn_context.collaboration_mode.clone()),
multi_agent_version: None,
realtime_active: Some(turn_context.realtime_active),
effort: turn_context.reasoning_effort,
effort: turn_context.reasoning_effort.clone(),
summary: codex_protocol::config_types::ReasoningSummary::Auto,
};
@@ -1308,7 +1308,7 @@ async fn record_initial_history_resumed_trailing_incomplete_turn_compaction_clea
collaboration_mode: Some(turn_context.collaboration_mode.clone()),
multi_agent_version: None,
realtime_active: Some(turn_context.realtime_active),
effort: turn_context.reasoning_effort,
effort: turn_context.reasoning_effort.clone(),
summary: codex_protocol::config_types::ReasoningSummary::Auto,
};
let previous_turn_id = previous_context_item
@@ -1467,7 +1467,7 @@ async fn record_initial_history_resumed_replaced_incomplete_compacted_turn_clear
collaboration_mode: Some(turn_context.collaboration_mode.clone()),
multi_agent_version: None,
realtime_active: Some(turn_context.realtime_active),
effort: turn_context.reasoning_effort,
effort: turn_context.reasoning_effort.clone(),
summary: codex_protocol::config_types::ReasoningSummary::Auto,
};
let previous_turn_id = previous_context_item
+10 -10
View File
@@ -2559,7 +2559,7 @@ async fn record_initial_history_forked_hydrates_previous_turn_settings() {
collaboration_mode: Some(turn_context.collaboration_mode.clone()),
multi_agent_version: None,
realtime_active: Some(turn_context.realtime_active),
effort: turn_context.reasoning_effort,
effort: turn_context.reasoning_effort.clone(),
summary: codex_protocol::config_types::ReasoningSummary::Auto,
};
let turn_id = previous_context_item
@@ -3146,7 +3146,7 @@ async fn set_rate_limits_retains_previous_credits() {
let model = get_model_offline_for_tests(config.model.as_deref());
let model_info =
construct_model_info_offline_for_tests(model.as_str(), &config.to_models_manager_config());
let reasoning_effort = config.model_reasoning_effort;
let reasoning_effort = config.model_reasoning_effort.clone();
let collaboration_mode = CollaborationMode {
mode: ModeKind::Default,
settings: Settings {
@@ -3254,7 +3254,7 @@ async fn set_rate_limits_updates_plan_type_when_present() {
let model = get_model_offline_for_tests(config.model.as_deref());
let model_info =
construct_model_info_offline_for_tests(model.as_str(), &config.to_models_manager_config());
let reasoning_effort = config.model_reasoning_effort;
let reasoning_effort = config.model_reasoning_effort.clone();
let collaboration_mode = CollaborationMode {
mode: ModeKind::Default,
settings: Settings {
@@ -3785,7 +3785,7 @@ pub(crate) async fn make_session_configuration_for_tests() -> SessionConfigurati
let model = get_model_offline_for_tests(config.model.as_deref());
let model_info =
construct_model_info_offline_for_tests(model.as_str(), &config.to_models_manager_config());
let reasoning_effort = config.model_reasoning_effort;
let reasoning_effort = config.model_reasoning_effort.clone();
let collaboration_mode = CollaborationMode {
mode: ModeKind::Default,
settings: Settings {
@@ -4614,7 +4614,7 @@ async fn session_new_fails_when_zsh_fork_enabled_without_packaged_zsh() {
mode: ModeKind::Default,
settings: Settings {
model,
reasoning_effort: config.model_reasoning_effort,
reasoning_effort: config.model_reasoning_effort.clone(),
developer_instructions: None,
},
};
@@ -4716,7 +4716,7 @@ pub(crate) async fn make_session_and_context() -> (Session, TurnContext) {
let model = get_model_offline_for_tests(config.model.as_deref());
let model_info =
construct_model_info_offline_for_tests(model.as_str(), &config.to_models_manager_config());
let reasoning_effort = config.model_reasoning_effort;
let reasoning_effort = config.model_reasoning_effort.clone();
let collaboration_mode = CollaborationMode {
mode: ModeKind::Default,
settings: Settings {
@@ -4959,7 +4959,7 @@ async fn make_session_with_config_and_rx(
mode: ModeKind::Default,
settings: Settings {
model,
reasoning_effort: config.model_reasoning_effort,
reasoning_effort: config.model_reasoning_effort.clone(),
developer_instructions: None,
},
};
@@ -5064,7 +5064,7 @@ async fn make_session_with_history_source_and_agent_control_and_rx(
mode: ModeKind::Default,
settings: Settings {
model,
reasoning_effort: config.model_reasoning_effort,
reasoning_effort: config.model_reasoning_effort.clone(),
developer_instructions: None,
},
};
@@ -5999,7 +5999,7 @@ async fn user_turn_updates_approvals_reviewer() {
mode: codex_protocol::config_types::ModeKind::Default,
settings: codex_protocol::config_types::Settings {
model: turn_context.model_info.slug.clone(),
reasoning_effort: config.model_reasoning_effort,
reasoning_effort: config.model_reasoning_effort.clone(),
developer_instructions: None,
},
}),
@@ -6806,7 +6806,7 @@ where
let model = get_model_offline_for_tests(config.model.as_deref());
let model_info =
construct_model_info_offline_for_tests(model.as_str(), &config.to_models_manager_config());
let reasoning_effort = config.model_reasoning_effort;
let reasoning_effort = config.model_reasoning_effort.clone();
let collaboration_mode = CollaborationMode {
mode: ModeKind::Default,
settings: Settings {
+2 -2
View File
@@ -698,7 +698,7 @@ async fn track_turn_resolved_config_analytics(
permission_profile: turn_context.permission_profile(),
#[allow(deprecated)]
permission_profile_cwd: turn_context.cwd.to_path_buf(),
reasoning_effort: turn_context.reasoning_effort,
reasoning_effort: turn_context.reasoning_effort.clone(),
reasoning_summary: Some(turn_context.reasoning_summary),
service_tier: turn_context
.config
@@ -1799,7 +1799,7 @@ async fn try_run_sampling_request(
prompt,
&turn_context.model_info,
&turn_context.session_telemetry,
turn_context.reasoning_effort,
turn_context.reasoning_effort.clone(),
turn_context.reasoning_summary,
turn_context.config.service_tier.clone(),
turn_metadata_header,
+12 -10
View File
@@ -136,7 +136,8 @@ impl TurnContext {
pub(crate) fn effective_reasoning_effort(&self) -> Option<ReasoningEffortConfig> {
if self.model_info.supports_reasoning_summaries {
self.reasoning_effort
.or(self.model_info.default_reasoning_level)
.clone()
.or_else(|| self.model_info.default_reasoning_level.clone())
} else {
None
}
@@ -196,28 +197,29 @@ impl TurnContext {
let supported_reasoning_levels = model_info
.supported_reasoning_levels
.iter()
.map(|preset| preset.effort)
.map(|preset| preset.effort.clone())
.collect::<Vec<_>>();
let reasoning_effort = if let Some(current_reasoning_effort) = self.reasoning_effort {
let reasoning_effort = if let Some(current_reasoning_effort) = self.reasoning_effort.clone()
{
if supported_reasoning_levels.contains(&current_reasoning_effort) {
Some(current_reasoning_effort)
} else {
supported_reasoning_levels
.get(supported_reasoning_levels.len().saturating_sub(1) / 2)
.copied()
.or(model_info.default_reasoning_level)
.cloned()
.or_else(|| model_info.default_reasoning_level.clone())
}
} else {
supported_reasoning_levels
.get(supported_reasoning_levels.len().saturating_sub(1) / 2)
.copied()
.or(model_info.default_reasoning_level)
.cloned()
.or_else(|| model_info.default_reasoning_level.clone())
};
config.model_reasoning_effort = reasoning_effort;
config.model_reasoning_effort = reasoning_effort.clone();
let collaboration_mode = self.collaboration_mode.with_updates(
Some(model.clone()),
Some(reasoning_effort),
Some(reasoning_effort.clone()),
/*developer_instructions*/ None,
);
let features = self.features.clone();
@@ -363,7 +365,7 @@ impl TurnContext {
collaboration_mode: Some(self.collaboration_mode.clone()),
multi_agent_version: Some(self.multi_agent_version),
realtime_active: Some(self.realtime_active),
effort: self.reasoning_effort,
effort: self.reasoning_effort.clone(),
summary: ReasoningSummaryConfig::Auto,
}
}
+1 -1
View File
@@ -271,7 +271,7 @@ async fn schedule_startup_prewarm_inner(
&startup_prompt,
&startup_turn_context.model_info,
&startup_turn_context.session_telemetry,
startup_turn_context.reasoning_effort,
startup_turn_context.reasoning_effort.clone(),
startup_turn_context.reasoning_summary,
startup_turn_context.config.service_tier.clone(),
startup_turn_metadata_header.as_deref(),
@@ -83,7 +83,7 @@ async fn handle_spawn_agent(
sender_thread_id: session.thread_id,
prompt: prompt.clone(),
model: args.model.clone().unwrap_or_default(),
reasoning_effort: args.reasoning_effort.unwrap_or_default(),
reasoning_effort: args.reasoning_effort.clone().unwrap_or_default(),
}
.into(),
)
@@ -94,14 +94,18 @@ async fn handle_spawn_agent(
config.service_tier = Some(service_tier.clone());
}
if args.fork_context {
reject_full_fork_spawn_overrides(role_name, args.model.as_deref(), args.reasoning_effort)?;
reject_full_fork_spawn_overrides(
role_name,
args.model.as_deref(),
args.reasoning_effort.clone(),
)?;
} else {
apply_requested_spawn_agent_model_overrides(
&session,
turn.as_ref(),
&mut config,
args.model.as_deref(),
args.reasoning_effort,
args.reasoning_effort.clone(),
)
.await?;
apply_role_to_config(&mut config, role_name)
@@ -174,7 +178,7 @@ async fn handle_spawn_agent(
.unwrap_or_else(|| args.model.clone().unwrap_or_default());
let effective_reasoning_effort = agent_snapshot
.as_ref()
.and_then(|snapshot| snapshot.reasoning_effort)
.and_then(|snapshot| snapshot.reasoning_effort.clone())
.unwrap_or(args.reasoning_effort.unwrap_or_default());
let nickname = new_agent_nickname.clone();
session
@@ -224,7 +224,8 @@ fn build_agent_shared_config(turn: &TurnContext) -> Result<Config, FunctionCallE
config.model_provider = turn.provider.info().clone();
config.model_reasoning_effort = turn
.reasoning_effort
.or(turn.model_info.default_reasoning_level);
.clone()
.or_else(|| turn.model_info.default_reasoning_level.clone());
config.model_reasoning_summary = Some(turn.reasoning_summary);
config.developer_instructions = turn.developer_instructions.clone();
config.compact_prompt = turn.compact_prompt.clone();
@@ -304,7 +305,7 @@ pub(crate) async fn apply_requested_spawn_agent_model_overrides(
validate_spawn_agent_reasoning_effort(
&selected_model_name,
&selected_model_info.supported_reasoning_levels,
reasoning_effort,
&reasoning_effort,
)?;
config.model_reasoning_effort = Some(reasoning_effort);
} else {
@@ -318,7 +319,7 @@ pub(crate) async fn apply_requested_spawn_agent_model_overrides(
validate_spawn_agent_reasoning_effort(
&turn.model_info.slug,
&turn.model_info.supported_reasoning_levels,
reasoning_effort,
&reasoning_effort,
)?;
config.model_reasoning_effort = Some(reasoning_effort);
}
@@ -404,11 +405,11 @@ fn find_spawn_agent_model_name(
fn validate_spawn_agent_reasoning_effort(
model: &str,
supported_reasoning_levels: &[ReasoningEffortPreset],
requested_reasoning_effort: ReasoningEffort,
requested_reasoning_effort: &ReasoningEffort,
) -> Result<(), FunctionCallError> {
if supported_reasoning_levels
.iter()
.any(|preset| preset.effort == requested_reasoning_effort)
.any(|preset| &preset.effort == requested_reasoning_effort)
{
return Ok(());
}
@@ -17,6 +17,7 @@ const SPAWN_AGENT_MODEL_OVERRIDE_DESCRIPTION: &str =
const SPAWN_AGENT_SERVICE_TIER_OVERRIDE_DESCRIPTION: &str =
"Service tier override for the new agent. Omit unless explicitly requested.";
const MAX_MODEL_OVERRIDES_IN_SPAWN_AGENT_DESCRIPTION: usize = 5;
const MAX_REASONING_EFFORT_CHARS_IN_SPAWN_AGENT_DESCRIPTION: usize = 64;
#[derive(Debug, Clone, Default)]
pub struct SpawnAgentToolOptions {
@@ -762,13 +763,20 @@ fn spawn_agent_models_description(models: &[ModelPreset]) -> String {
let model_descriptions = visible_models
.into_iter()
.map(|model| {
let default_reasoning_effort = model.default_reasoning_effort;
let default_reasoning_effort = &model.default_reasoning_effort;
let efforts = model
.supported_reasoning_efforts
.iter()
.map(|preset| {
let effort = preset.effort;
if effort == default_reasoning_effort {
let effort = preset.effort.as_str();
let effort = match effort
.char_indices()
.nth(MAX_REASONING_EFFORT_CHARS_IN_SPAWN_AGENT_DESCRIPTION)
{
Some((index, _)) => &effort[..index],
None => effort,
};
if &preset.effort == default_reasoning_effort {
format!("{effort} (default)")
} else {
effort.to_string()
@@ -186,6 +186,27 @@ fn spawn_agent_tool_caps_visible_model_summaries() {
assert!(!description.contains("`sixth-model`"));
}
#[test]
fn spawn_agent_tool_caps_reasoning_effort_value_length() {
let mut model = model_preset("visible", /*show_in_picker*/ true);
let custom_effort = ReasoningEffort::Custom(
"é".repeat(MAX_REASONING_EFFORT_CHARS_IN_SPAWN_AGENT_DESCRIPTION + 1),
);
model.default_reasoning_effort = custom_effort.clone();
model.supported_reasoning_efforts = vec![ReasoningEffortPreset {
effort: custom_effort,
description: "Model-defined".to_string(),
}];
assert_eq!(
spawn_agent_models_description(&[model]),
format!(
"Available model overrides (optional; inherited parent model is preferred):\n- `visible-model`: visible description Reasoning efforts: {} (default). Service tiers: priority.",
"é".repeat(MAX_REASONING_EFFORT_CHARS_IN_SPAWN_AGENT_DESCRIPTION)
)
);
}
#[test]
fn spawn_agent_tool_hides_service_tier_with_spawn_metadata() {
let tool = create_spawn_agent_tool_v2(SpawnAgentToolOptions {
@@ -4374,7 +4374,7 @@ async fn build_agent_spawn_config_uses_turn_context_values() {
expected.base_instructions = Some(base_instructions.text);
expected.model = Some(turn.model_info.slug.clone());
expected.model_provider = turn.provider.info().clone();
expected.model_reasoning_effort = turn.reasoning_effort;
expected.model_reasoning_effort = turn.reasoning_effort.clone();
expected.model_reasoning_summary = Some(turn.reasoning_summary);
expected.developer_instructions = turn.developer_instructions.clone();
expected.compact_prompt = turn.compact_prompt.clone();
@@ -4431,7 +4431,7 @@ async fn build_agent_resume_config_clears_base_instructions() {
expected.base_instructions = None;
expected.model = Some(turn.model_info.slug.clone());
expected.model_provider = turn.provider.info().clone();
expected.model_reasoning_effort = turn.reasoning_effort;
expected.model_reasoning_effort = turn.reasoning_effort.clone();
expected.model_reasoning_summary = Some(turn.reasoning_summary);
expected.developer_instructions = turn.developer_instructions.clone();
expected.compact_prompt = turn.compact_prompt.clone();
@@ -75,7 +75,7 @@ async fn handle_spawn_agent(
sender_thread_id: session.thread_id,
prompt: prompt.clone(),
model: args.model.clone().unwrap_or_default(),
reasoning_effort: args.reasoning_effort.unwrap_or_default(),
reasoning_effort: args.reasoning_effort.clone().unwrap_or_default(),
}
.into(),
)
@@ -86,14 +86,18 @@ async fn handle_spawn_agent(
config.service_tier = Some(service_tier.clone());
}
if matches!(fork_mode, Some(SpawnAgentForkMode::FullHistory)) {
reject_full_fork_spawn_overrides(role_name, args.model.as_deref(), args.reasoning_effort)?;
reject_full_fork_spawn_overrides(
role_name,
args.model.as_deref(),
args.reasoning_effort.clone(),
)?;
} else {
apply_requested_spawn_agent_model_overrides(
&session,
turn.as_ref(),
&mut config,
args.model.as_deref(),
args.reasoning_effort,
args.reasoning_effort.clone(),
)
.await?;
apply_role_to_config(&mut config, role_name)
@@ -188,7 +192,7 @@ async fn handle_spawn_agent(
.unwrap_or_else(|| args.model.clone().unwrap_or_default());
let effective_reasoning_effort = agent_snapshot
.as_ref()
.and_then(|snapshot| snapshot.reasoning_effort)
.and_then(|snapshot| snapshot.reasoning_effort.clone())
.unwrap_or(args.reasoning_effort.unwrap_or_default());
let nickname = new_agent_nickname.clone();
session
+3 -3
View File
@@ -74,7 +74,7 @@ async fn responses_stream_includes_subagent_header_on_review() {
let mut config = load_default_config_for_test(&codex_home).await;
config.model_provider_id = provider.name.clone();
config.model_provider = provider.clone();
let effort = config.model_reasoning_effort;
let effort = config.model_reasoning_effort.clone();
let summary = config.model_reasoning_summary;
let model = codex_core::test_support::get_model_offline(config.model.as_deref());
config.model = Some(model.clone());
@@ -202,7 +202,7 @@ async fn responses_stream_includes_subagent_header_on_other() {
let mut config = load_default_config_for_test(&codex_home).await;
config.model_provider_id = provider.name.clone();
config.model_provider = provider.clone();
let effort = config.model_reasoning_effort;
let effort = config.model_reasoning_effort.clone();
let summary = config.model_reasoning_summary;
let model = codex_core::test_support::get_model_offline(config.model.as_deref());
config.model = Some(model.clone());
@@ -318,7 +318,7 @@ async fn responses_respects_model_info_overrides_from_config() {
config.model_provider = provider.clone();
config.model_supports_reasoning_summaries = Some(true);
config.model_reasoning_summary = Some(ReasoningSummary::Detailed);
let effort = config.model_reasoning_effort;
let effort = config.model_reasoning_effort.clone();
let summary = config.model_reasoning_summary;
let model = config.model.clone().expect("model configured");
let config = Arc::new(config);
+2 -2
View File
@@ -876,7 +876,7 @@ async fn send_provider_auth_request(server: &MockServer, auth: ModelProviderAuth
let mut config = load_default_config_for_test(&codex_home).await;
config.model_provider_id = provider.name.clone();
config.model_provider = provider.clone();
let effort = config.model_reasoning_effort;
let effort = config.model_reasoning_effort.clone();
let summary = config.model_reasoning_summary;
let model = codex_core::test_support::get_model_offline(config.model.as_deref());
config.model = Some(model.clone());
@@ -2332,7 +2332,7 @@ async fn azure_responses_request_includes_store_and_reasoning_ids() {
let mut config = load_default_config_for_test(&codex_home).await;
config.model_provider_id = provider.name.clone();
config.model_provider = provider.clone();
let effort = config.model_reasoning_effort;
let effort = config.model_reasoning_effort.clone();
let summary = config.model_reasoning_summary;
let model = codex_core::test_support::get_model_offline(config.model.as_deref());
config.model = Some(model.clone());
+12 -12
View File
@@ -343,7 +343,7 @@ async fn responses_websocket_request_prewarm_reuses_connection() {
&prompt,
&harness.model_info,
&harness.session_telemetry,
harness.effort,
harness.effort.clone(),
harness.summary,
/*service_tier*/ None,
/*turn_metadata_header*/ None,
@@ -397,7 +397,7 @@ async fn responses_websocket_request_prewarm_traces_logical_request() {
&prompt,
&harness.model_info,
&harness.session_telemetry,
harness.effort,
harness.effort.clone(),
harness.summary,
/*service_tier*/ None,
/*turn_metadata_header*/ None,
@@ -442,7 +442,7 @@ async fn responses_websocket_request_prewarm_traces_logical_request() {
&prompt,
&harness.model_info,
&harness.session_telemetry,
harness.effort,
harness.effort.clone(),
harness.summary,
/*service_tier*/ None,
/*turn_metadata_header*/ None,
@@ -534,7 +534,7 @@ async fn responses_websocket_preconnect_is_reused_even_with_header_changes() {
&prompt,
&harness.model_info,
&harness.session_telemetry,
harness.effort,
harness.effort.clone(),
harness.summary,
/*service_tier*/ None,
/*turn_metadata_header*/ None,
@@ -573,7 +573,7 @@ async fn responses_websocket_request_prewarm_is_reused_even_with_header_changes(
&prompt,
&harness.model_info,
&harness.session_telemetry,
harness.effort,
harness.effort.clone(),
harness.summary,
/*service_tier*/ None,
/*turn_metadata_header*/ None,
@@ -585,7 +585,7 @@ async fn responses_websocket_request_prewarm_is_reused_even_with_header_changes(
&prompt,
&harness.model_info,
&harness.session_telemetry,
harness.effort,
harness.effort.clone(),
harness.summary,
/*service_tier*/ None,
/*turn_metadata_header*/ None,
@@ -639,7 +639,7 @@ async fn responses_websocket_prewarm_uses_v2_when_provider_supports_websockets()
&prompt,
&harness.model_info,
&harness.session_telemetry,
harness.effort,
harness.effort.clone(),
harness.summary,
/*service_tier*/ None,
/*turn_metadata_header*/ None,
@@ -988,7 +988,7 @@ async fn responses_websocket_emits_reasoning_included_event() {
&prompt,
&harness.model_info,
&harness.session_telemetry,
harness.effort,
harness.effort.clone(),
harness.summary,
/*service_tier*/ None,
/*turn_metadata_header*/ None,
@@ -1062,7 +1062,7 @@ async fn responses_websocket_emits_rate_limit_events() {
&prompt,
&harness.model_info,
&harness.session_telemetry,
harness.effort,
harness.effort.clone(),
harness.summary,
/*service_tier*/ None,
/*turn_metadata_header*/ None,
@@ -1718,7 +1718,7 @@ async fn responses_websocket_v2_after_error_uses_full_create_without_previous_re
&prompt_two,
&harness.model_info,
&harness.session_telemetry,
harness.effort,
harness.effort.clone(),
harness.summary,
/*service_tier*/ None,
/*turn_metadata_header*/ None,
@@ -1806,7 +1806,7 @@ async fn responses_websocket_v2_surfaces_terminal_error_without_close_handshake(
&prompt_two,
&harness.model_info,
&harness.session_telemetry,
harness.effort,
harness.effort.clone(),
harness.summary,
/*service_tier*/ None,
/*turn_metadata_header*/ None,
@@ -2073,7 +2073,7 @@ async fn stream_until_complete_with_request_metadata(
prompt,
&harness.model_info,
&harness.session_telemetry,
harness.effort,
harness.effort.clone(),
harness.summary,
service_tier.map(|service_tier| service_tier.request_value().to_string()),
turn_metadata_header,
+1 -1
View File
@@ -58,7 +58,7 @@ fn read_only_user_turn(test: &TestCodex, items: Vec<UserInput>, model: String) -
mode: codex_protocol::config_types::ModeKind::Default,
settings: codex_protocol::config_types::Settings {
model,
reasoning_effort: test.config.model_reasoning_effort,
reasoning_effort: test.config.model_reasoning_effort.clone(),
developer_instructions: None,
},
}),
@@ -135,7 +135,7 @@ async fn snapshot_model_visible_layout_turn_overrides() -> Result<()> {
mode: codex_protocol::config_types::ModeKind::Default,
settings: codex_protocol::config_types::Settings {
model: test.session_configured.model.clone(),
reasoning_effort: test.config.model_reasoning_effort,
reasoning_effort: test.config.model_reasoning_effort.clone(),
developer_instructions: None,
},
}),
@@ -172,7 +172,7 @@ async fn snapshot_model_visible_layout_turn_overrides() -> Result<()> {
mode: codex_protocol::config_types::ModeKind::Default,
settings: codex_protocol::config_types::Settings {
model: test.session_configured.model.clone(),
reasoning_effort: test.config.model_reasoning_effort,
reasoning_effort: test.config.model_reasoning_effort.clone(),
developer_instructions: None,
},
}),
@@ -261,7 +261,7 @@ async fn snapshot_model_visible_layout_cwd_change_does_not_refresh_agents() -> R
mode: codex_protocol::config_types::ModeKind::Default,
settings: codex_protocol::config_types::Settings {
model: test.session_configured.model.clone(),
reasoning_effort: test.config.model_reasoning_effort,
reasoning_effort: test.config.model_reasoning_effort.clone(),
developer_instructions: None,
},
}),
@@ -295,7 +295,7 @@ async fn snapshot_model_visible_layout_cwd_change_does_not_refresh_agents() -> R
mode: codex_protocol::config_types::ModeKind::Default,
settings: codex_protocol::config_types::Settings {
model: test.session_configured.model.clone(),
reasoning_effort: test.config.model_reasoning_effort,
reasoning_effort: test.config.model_reasoning_effort.clone(),
developer_instructions: None,
},
}),
@@ -422,7 +422,7 @@ async fn snapshot_model_visible_layout_resume_with_personality_change() -> Resul
mode: codex_protocol::config_types::ModeKind::Default,
settings: codex_protocol::config_types::Settings {
model: resumed.session_configured.model.clone(),
reasoning_effort: resumed.config.model_reasoning_effort,
reasoning_effort: resumed.config.model_reasoning_effort.clone(),
developer_instructions: None,
},
}),
+1 -1
View File
@@ -79,7 +79,7 @@ fn read_only_text_turn_with_personality(
mode: codex_protocol::config_types::ModeKind::Default,
settings: codex_protocol::config_types::Settings {
model,
reasoning_effort: test.config.model_reasoning_effort,
reasoning_effort: test.config.model_reasoning_effort.clone(),
developer_instructions: None,
},
}),
+3 -3
View File
@@ -870,7 +870,7 @@ async fn send_user_turn_with_no_changes_does_not_send_environment_context() -> a
let default_approval_policy = config.permissions.approval_policy.value();
let default_sandbox_policy = &config.legacy_sandbox_policy();
let default_model = session_configured.model;
let default_effort = config.model_reasoning_effort;
let default_effort = config.model_reasoning_effort.clone();
let default_summary = config.model_reasoning_summary;
codex
@@ -892,7 +892,7 @@ async fn send_user_turn_with_no_changes_does_not_send_environment_context() -> a
mode: codex_protocol::config_types::ModeKind::Default,
settings: codex_protocol::config_types::Settings {
model: default_model.clone(),
reasoning_effort: default_effort,
reasoning_effort: default_effort.clone(),
developer_instructions: None,
},
}),
@@ -1013,7 +1013,7 @@ async fn send_user_turn_with_changes_sends_environment_context() -> anyhow::Resu
let default_approval_policy = config.permissions.approval_policy.value();
let default_sandbox_policy = &config.legacy_sandbox_policy();
let default_model = session_configured.model;
let default_effort = config.model_reasoning_effort;
let default_effort = config.model_reasoning_effort.clone();
let default_summary = config.model_reasoning_summary;
codex
+6 -5
View File
@@ -318,7 +318,7 @@ async fn remote_models_use_context_window_when_config_override_is_absent() -> Re
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn remote_models_long_model_slug_is_sent_with_high_reasoning() -> Result<()> {
async fn remote_models_long_model_slug_is_sent_with_custom_reasoning() -> Result<()> {
skip_if_no_network!(Ok(()));
skip_if_sandbox!(Ok(()));
@@ -331,15 +331,16 @@ async fn remote_models_long_model_slug_is_sent_with_high_reasoning() -> Result<(
/*priority*/ 1_000,
TruncationPolicyConfig::bytes(/*limit*/ 10_000),
);
remote_model.default_reasoning_level = Some(ReasoningEffort::High);
let custom_reasoning_effort = ReasoningEffort::Custom("max".to_string());
remote_model.default_reasoning_level = Some(custom_reasoning_effort.clone());
remote_model.supported_reasoning_levels = vec![
ReasoningEffortPreset {
effort: ReasoningEffort::Medium,
description: ReasoningEffort::Medium.to_string(),
},
ReasoningEffortPreset {
effort: ReasoningEffort::High,
description: ReasoningEffort::High.to_string(),
effort: custom_reasoning_effort.clone(),
description: custom_reasoning_effort.to_string(),
},
];
remote_model.supports_reasoning_summaries = true;
@@ -393,7 +394,7 @@ async fn remote_models_long_model_slug_is_sent_with_high_reasoning() -> Result<(
.and_then(|reasoning| reasoning.get("summary"))
.and_then(|value| value.as_str());
assert_eq!(body["model"].as_str(), Some(requested_model));
assert_eq!(reasoning_effort, Some("high"));
assert_eq!(reasoning_effort, Some("max"));
assert_eq!(reasoning_summary, Some("detailed"));
Ok(())
+1 -1
View File
@@ -41,7 +41,7 @@ fn resume_history(
collaboration_mode: None,
multi_agent_version: None,
realtime_active: None,
effort: config.model_reasoning_effort,
effort: config.model_reasoning_effort.clone(),
summary: config
.model_reasoning_summary
.unwrap_or(ReasoningSummary::Auto),
@@ -55,7 +55,7 @@ fn disabled_text_turn(test: &TestCodex, text: &str) -> Op {
mode: codex_protocol::config_types::ModeKind::Default,
settings: codex_protocol::config_types::Settings {
model: REQUESTED_MODEL.to_string(),
reasoning_effort: test.config.model_reasoning_effort,
reasoning_effort: test.config.model_reasoning_effort.clone(),
developer_instructions: None,
},
}),