diff --git a/codex-rs/core/src/compact.rs b/codex-rs/core/src/compact.rs index 04f3726ff0..8ec6838a84 100644 --- a/codex-rs/core/src/compact.rs +++ b/codex-rs/core/src/compact.rs @@ -676,7 +676,7 @@ async fn drain_to_completed( prompt, &turn_context.model_info, &turn_context.session_telemetry, - step_context.turn.reasoning_effort.clone(), + step_context.reasoning_effort.clone(), turn_context.reasoning_summary, turn_context.config.service_tier.clone(), responses_metadata, diff --git a/codex-rs/core/src/compact_remote_request.rs b/codex-rs/core/src/compact_remote_request.rs index 296c7565aa..1ab9213f00 100644 --- a/codex-rs/core/src/compact_remote_request.rs +++ b/codex-rs/core/src/compact_remote_request.rs @@ -87,7 +87,7 @@ pub(super) async fn run_remote_compact_attempt( &turn_context.model_info, turn_state, CompactConversationRequestSettings { - effort: step_context.turn.reasoning_effort.clone(), + effort: step_context.reasoning_effort.clone(), summary: turn_context.reasoning_summary, service_tier: if sess.services.auth_manager.auth_mode() == Some(AuthMode::ApiKey) { None diff --git a/codex-rs/core/src/compact_remote_v2.rs b/codex-rs/core/src/compact_remote_v2.rs index 52f359db22..522683a0bb 100644 --- a/codex-rs/core/src/compact_remote_v2.rs +++ b/codex-rs/core/src/compact_remote_v2.rs @@ -345,7 +345,7 @@ async fn run_remote_compaction_request_v2( prompt, &turn_context.model_info, &turn_context.session_telemetry, - step_context.turn.reasoning_effort.clone(), + step_context.reasoning_effort.clone(), turn_context.reasoning_summary, turn_context.config.service_tier.clone(), responses_metadata, diff --git a/codex-rs/core/src/guardian/review.rs b/codex-rs/core/src/guardian/review.rs index d957a71335..4bc1cee740 100644 --- a/codex-rs/core/src/guardian/review.rs +++ b/codex-rs/core/src/guardian/review.rs @@ -729,7 +729,6 @@ pub(super) async fn guardian_review_session_config( .iter() .any(|preset| preset.effort == codex_protocol::openai_models::ReasoningEffort::Low), step_context - .turn .reasoning_effort .clone() .or_else(|| turn.model_info.default_reasoning_level.clone()), diff --git a/codex-rs/core/src/guardian/review_session.rs b/codex-rs/core/src/guardian/review_session.rs index f637c73b66..fc7609e53e 100644 --- a/codex-rs/core/src/guardian/review_session.rs +++ b/codex-rs/core/src/guardian/review_session.rs @@ -1208,7 +1208,7 @@ mod tests { let turn = Arc::new(turn); let step_context = StepContext::for_test(Arc::clone(&turn)); let model = turn.model_info.slug.clone(); - let reasoning_effort = step_context.turn.reasoning_effort.clone(); + let reasoning_effort = step_context.reasoning_effort.clone(); let reasoning_summary = turn.reasoning_summary; let personality = turn.personality; #[allow(deprecated)] diff --git a/codex-rs/core/src/mcp_tool_call.rs b/codex-rs/core/src/mcp_tool_call.rs index f9269744f1..f84ad88aca 100644 --- a/codex-rs/core/src/mcp_tool_call.rs +++ b/codex-rs/core/src/mcp_tool_call.rs @@ -1092,7 +1092,7 @@ fn build_mcp_tool_call_request_meta( .turn_metadata_state .current_meta_value_for_mcp_request(McpTurnMetadataContext { model: turn_context.model_info.slug.as_str(), - reasoning_effort: step_context.turn.effective_reasoning_effort(), + reasoning_effort: step_context.effective_reasoning_effort(), }) { request_meta.insert( diff --git a/codex-rs/core/src/mcp_tool_call_tests.rs b/codex-rs/core/src/mcp_tool_call_tests.rs index 35fb630460..cd9b66a712 100644 --- a/codex-rs/core/src/mcp_tool_call_tests.rs +++ b/codex-rs/core/src/mcp_tool_call_tests.rs @@ -96,7 +96,7 @@ fn approval_metadata( fn mcp_turn_metadata_context(step_context: &StepContext) -> McpTurnMetadataContext<'_> { McpTurnMetadataContext { model: step_context.turn.model_info.slug.as_str(), - reasoning_effort: step_context.turn.effective_reasoning_effort(), + reasoning_effort: step_context.effective_reasoning_effort(), } } @@ -1118,7 +1118,6 @@ async fn mcp_tool_call_request_meta_includes_turn_metadata_for_custom_server() { .get("reasoning_effort") .and_then(serde_json::Value::as_str), step_context - .turn .effective_reasoning_effort() .map(|effort| effort.to_string()) .as_deref() diff --git a/codex-rs/core/src/session/mod.rs b/codex-rs/core/src/session/mod.rs index 88a996758b..2ab264493f 100644 --- a/codex-rs/core/src/session/mod.rs +++ b/codex-rs/core/src/session/mod.rs @@ -344,7 +344,6 @@ use codex_protocol::config_types::WindowsSandboxLevel; use codex_protocol::models::LocalImagePreparation; use codex_protocol::models::ResponseInputItem; use codex_protocol::models::ResponseItem; -use codex_protocol::openai_models::ReasoningEffort as ReasoningEffortConfig; use codex_protocol::protocol::ApplyPatchApprovalRequestEvent; use codex_protocol::protocol::AskForApproval; use codex_protocol::protocol::CodexErrorInfo; diff --git a/codex-rs/core/src/session/multi_agents.rs b/codex-rs/core/src/session/multi_agents.rs index b2b1b5719f..d5e91beed2 100644 --- a/codex-rs/core/src/session/multi_agents.rs +++ b/codex-rs/core/src/session/multi_agents.rs @@ -51,7 +51,7 @@ pub(crate) fn effective_multi_agent_mode(step_context: &StepContext) -> Option MultiAgentMode::Custom(hint_text.clone()), - None => match step_context.turn.effective_reasoning_effort() { + None => match step_context.effective_reasoning_effort() { Some(ReasoningEffort::Ultra) => MultiAgentMode::Proactive, _ => MultiAgentMode::ExplicitRequestOnly, }, diff --git a/codex-rs/core/src/session/review.rs b/codex-rs/core/src/session/review.rs index 9a89c1beeb..40ffe14721 100644 --- a/codex-rs/core/src/session/review.rs +++ b/codex-rs/core/src/session/review.rs @@ -73,7 +73,6 @@ 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.clone(); let reasoning_summary = per_turn_config .model_reasoning_summary .unwrap_or(model_info.default_reasoning_summary); @@ -117,7 +116,6 @@ pub(super) async fn spawn_review_thread( model_info: model_info.clone(), session_telemetry: session_telemetry_for_context, provider: provider_for_context, - reasoning_effort, reasoning_summary, session_source, history_mode: parent_turn_context.history_mode, diff --git a/codex-rs/core/src/session/rollout_reconstruction_tests.rs b/codex-rs/core/src/session/rollout_reconstruction_tests.rs index 0d7e6fe335..4d67be9c69 100644 --- a/codex-rs/core/src/session/rollout_reconstruction_tests.rs +++ b/codex-rs/core/src/session/rollout_reconstruction_tests.rs @@ -191,7 +191,6 @@ async fn record_initial_history_resumed_bare_turn_context_does_not_hydrate_previ multi_agent_mode: None, realtime_active: Some(turn_context.realtime_active), effort: test_step_context(turn_context.clone()) - .turn .reasoning_effort .clone(), summary: codex_protocol::config_types::ReasoningSummary::Auto, @@ -242,7 +241,6 @@ async fn record_initial_history_resumed_hydrates_previous_turn_settings_from_lif multi_agent_mode: None, realtime_active: Some(turn_context.realtime_active), effort: test_step_context(turn_context.clone()) - .turn .reasoning_effort .clone(), summary: codex_protocol::config_types::ReasoningSummary::Auto, @@ -1296,7 +1294,6 @@ async fn record_initial_history_resumed_turn_context_after_compaction_reestablis multi_agent_mode: None, realtime_active: Some(turn_context.realtime_active), effort: test_step_context(turn_context.clone()) - .turn .reasoning_effort .clone(), summary: codex_protocol::config_types::ReasoningSummary::Auto, @@ -1386,7 +1383,6 @@ async fn record_initial_history_resumed_turn_context_after_compaction_reestablis multi_agent_mode: None, realtime_active: Some(turn_context.realtime_active), effort: test_step_context(turn_context.clone()) - .turn .reasoning_effort .clone(), summary: codex_protocol::config_types::ReasoningSummary::Auto, @@ -1422,7 +1418,6 @@ async fn record_initial_history_resumed_aborted_turn_without_id_clears_active_tu multi_agent_mode: None, realtime_active: Some(turn_context.realtime_active), effort: test_step_context(turn_context.clone()) - .turn .reasoning_effort .clone(), summary: codex_protocol::config_types::ReasoningSummary::Auto, @@ -1553,7 +1548,6 @@ async fn record_initial_history_resumed_unmatched_abort_preserves_active_turn_fo multi_agent_mode: None, realtime_active: Some(turn_context.realtime_active), effort: test_step_context(turn_context.clone()) - .turn .reasoning_effort .clone(), summary: codex_protocol::config_types::ReasoningSummary::Auto, @@ -1679,7 +1673,6 @@ async fn record_initial_history_resumed_trailing_incomplete_turn_compaction_clea multi_agent_mode: None, realtime_active: Some(turn_context.realtime_active), effort: test_step_context(turn_context.clone()) - .turn .reasoning_effort .clone(), summary: codex_protocol::config_types::ReasoningSummary::Auto, @@ -1852,7 +1845,6 @@ async fn record_initial_history_resumed_replaced_incomplete_compacted_turn_clear multi_agent_mode: None, realtime_active: Some(turn_context.realtime_active), effort: test_step_context(turn_context.clone()) - .turn .reasoning_effort .clone(), summary: codex_protocol::config_types::ReasoningSummary::Auto, diff --git a/codex-rs/core/src/session/step_context.rs b/codex-rs/core/src/session/step_context.rs index 38fd2a2b33..3d2083ac0e 100644 --- a/codex-rs/core/src/session/step_context.rs +++ b/codex-rs/core/src/session/step_context.rs @@ -14,6 +14,7 @@ use tokio::sync::OnceCell; #[derive(Debug)] pub(crate) struct StepContext { pub(crate) turn: Arc, + pub(crate) reasoning_effort: Option, pub(crate) environments: TurnEnvironmentSnapshot, /// Capability roots bound to ready environments in this exact step. pub(crate) selected_capability_roots: Vec, @@ -26,6 +27,24 @@ pub(crate) struct StepContext { } impl StepContext { + pub(crate) fn effective_reasoning_effort( + &self, + ) -> Option { + if self.turn.model_info.supports_reasoning_summaries { + self.reasoning_effort + .clone() + .or_else(|| self.turn.model_info.default_reasoning_level.clone()) + } else { + None + } + } + + pub(crate) fn effective_reasoning_effort_for_tracing(&self) -> String { + self.effective_reasoning_effort() + .map(|effort| effort.to_string()) + .unwrap_or_else(|| "default".to_string()) + } + pub(crate) fn new( turn: Arc, environments: TurnEnvironmentSnapshot, @@ -33,8 +52,10 @@ impl StepContext { mcp: Arc, loaded_agents_md: Option>, ) -> Self { + let reasoning_effort = turn.config.model_reasoning_effort.clone(); Self { turn, + reasoning_effort, environments, selected_capability_roots, mcp, diff --git a/codex-rs/core/src/session/tests.rs b/codex-rs/core/src/session/tests.rs index 1c256ba424..e503edffb1 100644 --- a/codex-rs/core/src/session/tests.rs +++ b/codex-rs/core/src/session/tests.rs @@ -3108,7 +3108,6 @@ async fn record_initial_history_forked_hydrates_previous_turn_settings() { multi_agent_mode: None, realtime_active: Some(turn_context.realtime_active), effort: test_step_context(turn_context.clone()) - .turn .reasoning_effort .clone(), summary: codex_protocol::config_types::ReasoningSummary::Auto, @@ -3993,10 +3992,13 @@ async fn includes_timed_out_message() { #[tokio::test] async fn turn_context_with_model_updates_model_fields() { let (session, mut turn_context) = make_session_and_context().await; - turn_context.reasoning_effort = Some(ReasoningEffortConfig::Minimal); + Arc::make_mut(&mut turn_context.config).model_reasoning_effort = + Some(codex_protocol::openai_models::ReasoningEffort::Minimal); let updated = turn_context .with_model("gpt-5.4".to_string(), &session.services.models_manager) .await; + let updated = Arc::new(updated); + let updated_step = test_step_context(updated.clone()); let expected_model_info = session .services .models_manager @@ -4010,16 +4012,16 @@ async fn turn_context_with_model_updates_model_fields() { assert_eq!(updated.collaboration_mode().model(), "gpt-5.4"); assert_eq!(updated.model_info, expected_model_info); assert_eq!( - updated.reasoning_effort, - Some(ReasoningEffortConfig::Medium) + updated_step.reasoning_effort, + Some(codex_protocol::openai_models::ReasoningEffort::Medium) ); assert_eq!( updated.collaboration_mode().reasoning_effort(), - Some(ReasoningEffortConfig::Medium) + Some(codex_protocol::openai_models::ReasoningEffort::Medium) ); assert_eq!( updated.config.model_reasoning_effort, - Some(ReasoningEffortConfig::Medium) + Some(codex_protocol::openai_models::ReasoningEffort::Medium) ); } diff --git a/codex-rs/core/src/session/turn.rs b/codex-rs/core/src/session/turn.rs index 6908914291..233d36f9a5 100644 --- a/codex-rs/core/src/session/turn.rs +++ b/codex-rs/core/src/session/turn.rs @@ -785,7 +785,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: first_step_context.turn.reasoning_effort.clone(), + reasoning_effort: first_step_context.reasoning_effort.clone(), reasoning_summary: Some(turn_context.reasoning_summary), service_tier: turn_context .config @@ -1971,7 +1971,7 @@ async fn try_run_sampling_request( model = turn_context.model_info.slug.clone(), approval_policy = turn_context.approval_policy.value(), sandbox_policy = &turn_context.sandbox_policy(), - effort = step_context.turn.reasoning_effort, + effort = step_context.reasoning_effort, auth_mode = sess.services.auth_manager.auth_mode(), features = sess.features.enabled_features(), ); @@ -1991,7 +1991,7 @@ async fn try_run_sampling_request( prompt, &turn_context.model_info, &turn_context.session_telemetry, - step_context.turn.reasoning_effort.clone(), + step_context.reasoning_effort.clone(), turn_context.reasoning_summary, turn_context.config.service_tier.clone(), responses_metadata, @@ -2011,7 +2011,7 @@ async fn try_run_sampling_request( )> = None; let mut should_emit_turn_diff = false; let mut should_emit_token_count = false; - let reasoning_effort = step_context.turn.effective_reasoning_effort_for_tracing(); + let reasoning_effort = step_context.effective_reasoning_effort_for_tracing(); let plan_mode = turn_context.mode == ModeKind::Plan; let mut assistant_message_stream_parsers = AssistantMessageStreamParsers::new(plan_mode); let mut plan_mode_state = plan_mode.then(|| PlanModeStreamState::new(&turn_context.sub_id)); diff --git a/codex-rs/core/src/session/turn_context.rs b/codex-rs/core/src/session/turn_context.rs index e5ede2f2cc..66d52f2f53 100644 --- a/codex-rs/core/src/session/turn_context.rs +++ b/codex-rs/core/src/session/turn_context.rs @@ -109,7 +109,6 @@ pub struct TurnContext { pub(crate) model_info: ModelInfo, pub(crate) session_telemetry: SessionTelemetry, pub(crate) provider: SharedModelProvider, - pub(crate) reasoning_effort: Option, pub(crate) reasoning_summary: ReasoningSummaryConfig, pub(crate) session_source: SessionSource, pub(crate) history_mode: ThreadHistoryMode, @@ -162,7 +161,7 @@ impl TurnContext { mode: self.mode, settings: Settings { model: self.model_info.slug.clone(), - reasoning_effort: self.reasoning_effort.clone(), + reasoning_effort: self.config.model_reasoning_effort.clone(), developer_instructions: self.collaboration_mode_developer_instructions.clone(), }, } @@ -188,22 +187,6 @@ impl TurnContext { ) } - pub(crate) fn effective_reasoning_effort(&self) -> Option { - if self.model_info.supports_reasoning_summaries { - self.reasoning_effort - .clone() - .or_else(|| self.model_info.default_reasoning_level.clone()) - } else { - None - } - } - - pub(crate) fn effective_reasoning_effort_for_tracing(&self) -> String { - self.effective_reasoning_effort() - .map(|effort| effort.to_string()) - .unwrap_or_else(|| "default".to_string()) - } - pub(crate) fn model_context_window(&self) -> Option { let effective_context_window_percent = self.model_info.effective_context_window_percent; self.model_info @@ -239,22 +222,22 @@ impl TurnContext { .iter() .map(|preset| preset.effort.clone()) .collect::>(); - let reasoning_effort = if let Some(current_reasoning_effort) = self.reasoning_effort.clone() - { - if supported_reasoning_levels.contains(¤t_reasoning_effort) { - Some(current_reasoning_effort) + let reasoning_effort = + if let Some(current_reasoning_effort) = self.config.model_reasoning_effort.clone() { + if supported_reasoning_levels.contains(¤t_reasoning_effort) { + Some(current_reasoning_effort) + } else { + supported_reasoning_levels + .get(supported_reasoning_levels.len().saturating_sub(1) / 2) + .cloned() + .or_else(|| model_info.default_reasoning_level.clone()) + } } else { supported_reasoning_levels .get(supported_reasoning_levels.len().saturating_sub(1) / 2) .cloned() .or_else(|| model_info.default_reasoning_level.clone()) - } - } else { - supported_reasoning_levels - .get(supported_reasoning_levels.len().saturating_sub(1) / 2) - .cloned() - .or_else(|| model_info.default_reasoning_level.clone()) - }; + }; config.model_reasoning_effort = reasoning_effort.clone(); let available_models = models_manager @@ -276,7 +259,6 @@ impl TurnContext { .clone() .with_model(model.as_str(), model_info.slug.as_str()), provider: self.provider.clone(), - reasoning_effort, reasoning_summary: self.reasoning_summary, session_source: self.session_source.clone(), history_mode: self.history_mode, @@ -397,14 +379,14 @@ impl StepContext { mode: turn.mode, settings: Settings { model: turn.model_info.slug.clone(), - reasoning_effort: turn.reasoning_effort.clone(), + reasoning_effort: self.reasoning_effort.clone(), developer_instructions: turn.collaboration_mode_developer_instructions.clone(), }, }), multi_agent_version: Some(turn.multi_agent_version), multi_agent_mode: super::multi_agents::effective_multi_agent_mode(self), realtime_active: Some(turn.realtime_active), - effort: turn.reasoning_effort.clone(), + effort: self.reasoning_effort.clone(), summary: ReasoningSummaryConfig::Auto, } } @@ -520,7 +502,6 @@ impl Session { skills_snapshot: HostSkillsSnapshot, ) -> TurnContext { let collaboration_mode = &session_configuration.collaboration_mode; - let reasoning_effort = collaboration_mode.reasoning_effort(); let reasoning_summary = session_configuration .model_reasoning_summary .unwrap_or(model_info.default_reasoning_summary); @@ -572,7 +553,6 @@ impl Session { model_info, session_telemetry: session_telemetry_for_context, provider: provider_for_context, - reasoning_effort, reasoning_summary, session_source, history_mode: session_configuration.history_mode, diff --git a/codex-rs/core/src/session_startup_prewarm.rs b/codex-rs/core/src/session_startup_prewarm.rs index c6530bfb8f..677c9859ed 100644 --- a/codex-rs/core/src/session_startup_prewarm.rs +++ b/codex-rs/core/src/session_startup_prewarm.rs @@ -311,7 +311,7 @@ async fn schedule_startup_prewarm_inner( &startup_prompt, &startup_turn_context.model_info, &startup_turn_context.session_telemetry, - step_context.turn.reasoning_effort.clone(), + step_context.reasoning_effort.clone(), startup_turn_context.reasoning_summary, startup_turn_context.config.service_tier.clone(), &responses_metadata, diff --git a/codex-rs/core/src/tasks/mod.rs b/codex-rs/core/src/tasks/mod.rs index 7279461b19..21cb42a69f 100644 --- a/codex-rs/core/src/tasks/mod.rs +++ b/codex-rs/core/src/tasks/mod.rs @@ -384,7 +384,17 @@ impl Session { let task_cancellation_token = cancellation_token.child_token(); // Task-owned turn spans keep a core-owned span open for the // full task lifecycle after the submission dispatch span ends. - let reasoning_effort = turn_context.effective_reasoning_effort_for_tracing(); + let reasoning_effort = if turn_context.model_info.supports_reasoning_summaries { + turn_context + .config + .model_reasoning_effort + .clone() + .or_else(|| turn_context.model_info.default_reasoning_level.clone()) + .map(|effort| effort.to_string()) + .unwrap_or_else(|| "default".to_string()) + } else { + "default".to_string() + }; let task_span = info_span!( "turn", otel.name = span_name, diff --git a/codex-rs/core/src/tools/handlers/multi_agents_common.rs b/codex-rs/core/src/tools/handlers/multi_agents_common.rs index 68586c82bd..49af083d02 100644 --- a/codex-rs/core/src/tools/handlers/multi_agents_common.rs +++ b/codex-rs/core/src/tools/handlers/multi_agents_common.rs @@ -184,7 +184,6 @@ fn build_agent_shared_config(step_context: &StepContext) -> Result