Move reasoning effort into step context

This commit is contained in:
Ahmed Ibrahim
2026-07-09 16:30:30 -07:00
parent 9f8d7de0e7
commit 91dcea3bbe
19 changed files with 68 additions and 69 deletions
+1 -1
View File
@@ -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,
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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,
-1
View File
@@ -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()),
+1 -1
View File
@@ -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)]
+1 -1
View File
@@ -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(
+1 -2
View File
@@ -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()
-1
View File
@@ -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;
+1 -1
View File
@@ -51,7 +51,7 @@ pub(crate) fn effective_multi_agent_mode(step_context: &StepContext) -> Option<M
.multi_agent_mode_hint_text
{
Some(hint_text) => 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,
},
-2
View File
@@ -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,
@@ -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,
+21
View File
@@ -14,6 +14,7 @@ use tokio::sync::OnceCell;
#[derive(Debug)]
pub(crate) struct StepContext {
pub(crate) turn: Arc<TurnContext>,
pub(crate) reasoning_effort: Option<codex_protocol::openai_models::ReasoningEffort>,
pub(crate) environments: TurnEnvironmentSnapshot,
/// Capability roots bound to ready environments in this exact step.
pub(crate) selected_capability_roots: Vec<ResolvedSelectedCapabilityRoot>,
@@ -26,6 +27,24 @@ pub(crate) struct StepContext {
}
impl StepContext {
pub(crate) fn effective_reasoning_effort(
&self,
) -> Option<codex_protocol::openai_models::ReasoningEffort> {
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<TurnContext>,
environments: TurnEnvironmentSnapshot,
@@ -33,8 +52,10 @@ impl StepContext {
mcp: Arc<McpRuntimeSnapshot>,
loaded_agents_md: Option<Arc<LoadedAgentsMd>>,
) -> Self {
let reasoning_effort = turn.config.model_reasoning_effort.clone();
Self {
turn,
reasoning_effort,
environments,
selected_capability_roots,
mcp,
+8 -6
View File
@@ -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)
);
}
+4 -4
View File
@@ -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));
+14 -34
View File
@@ -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<ReasoningEffortConfig>,
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<ReasoningEffortConfig> {
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<i64> {
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::<Vec<_>>();
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)
let reasoning_effort =
if let Some(current_reasoning_effort) = self.config.model_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)
.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,
+1 -1
View File
@@ -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,
+11 -1
View File
@@ -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,
@@ -184,7 +184,6 @@ fn build_agent_shared_config(step_context: &StepContext) -> Result<Config, Funct
config.model = Some(turn.model_info.slug.clone());
config.model_provider = turn.provider.info().clone();
config.model_reasoning_effort = step_context
.turn
.reasoning_effort
.clone()
.or_else(|| turn.model_info.default_reasoning_level.clone());
@@ -4520,7 +4520,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 = step_context.turn.reasoning_effort.clone();
expected.model_reasoning_effort = step_context.reasoning_effort.clone();
expected.model_reasoning_summary = Some(turn.reasoning_summary);
expected.developer_instructions = turn.developer_instructions.clone();
#[allow(deprecated)]
@@ -4557,7 +4557,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 = step_context.turn.reasoning_effort.clone();
expected.model_reasoning_effort = step_context.reasoning_effort.clone();
expected.model_reasoning_summary = Some(turn.reasoning_summary);
expected.developer_instructions = turn.developer_instructions.clone();
#[allow(deprecated)]