Pin request reasoning effort while configuration overrides are active (#43795)

## Why

Reasoning-effort changes should preserve the request baseline while trusted `configuration_update` items carry the selected effort.

## What changed

- Pin request-level reasoning effort for the current model when reasoning-effort overrides are available, including during WebSocket prewarm.
- Re-establish the selected effort after rollback, resume, or a model switch, refreshing the trusted override even when the selection is unchanged.
- Continue using the selected request effort when overrides are unavailable.

## Testing

Add regression coverage for WebSocket prewarm and reconnect, rollback, resume, and model switches. Update transition tests to assert that request-level effort stays pinned while configuration updates change.

GitOrigin-RevId: 89cfee6289543230347b0488d1388190b4da7216
This commit is contained in:
felixxia-oai
2026-09-08 10:41:15 +00:00
committed by copyberry
parent df522cae16
commit 31ccaf40c2
7 changed files with 504 additions and 14 deletions
+3
View File
@@ -299,6 +299,7 @@ use crate::state::AcceptedUserInputResponse;
use crate::state::AutoCompactWindowIds;
use crate::state::AutoCompactWindowSnapshot;
use crate::state::PendingRequestPermissions;
use crate::state::ReasoningEffortPin;
use crate::state::SessionServices;
use crate::state::SessionState;
#[cfg(test)]
@@ -1605,6 +1606,8 @@ impl Session {
state
.history
.restore_review_context(Some(&retained_context), guardian_history.as_ref());
// The next send supplies the selected effort. Refresh its trusted override too.
state.reasoning_effort_pin = ReasoningEffortPin::Unset;
if let Some(world_state) = world_state_baseline {
state.history.set_world_state_baseline(world_state);
}
+30 -4
View File
@@ -1,11 +1,12 @@
//! Gated reasoning-effort updates appended after accepted input.
//! Cache-preserving effort updates and the original sampling request effort.
//!
//! Only trusted harness items establish an effort. Updates append to surviving
//! history without replacing the request-level reasoning effort.
//! Only trusted harness items establish overrides. Replay invalidates the runtime
//! pin so the next send re-establishes the currently selected effort.
use super::session::Session;
use super::step_context::StepContext;
use super::step_settings::ResolvedStepSettings;
use crate::state::ReasoningEffortPin;
use codex_features::Feature;
use codex_history::CodexHarnessMetadata;
use codex_history::ResponseItemEnvelope;
@@ -43,7 +44,10 @@ impl Session {
_ => None,
}
});
established_effort == Some(&effort)
state
.reasoning_effort_pin
.get(&settings.model_info.slug)
.is_some_and(|pinned| established_effort.unwrap_or(&pinned) == &effort)
};
if should_skip {
return;
@@ -64,6 +68,28 @@ impl Session {
.await;
}
/// Reuses the original request effort while updates carry the selected effort.
pub(crate) async fn reasoning_effort_for_request(
&self,
settings: &ResolvedStepSettings,
) -> Option<ReasoningEffort> {
let selected_effort = settings.reasoning_effort().cloned();
if !self.enabled(Feature::ReasoningEffortOverride) {
return selected_effort;
}
let effort = self.effort_for_configuration_update(settings).await;
let mut state = self.state.lock().await;
let Some(effort) = effort else {
state.reasoning_effort_pin = ReasoningEffortPin::Unset;
return selected_effort;
};
Some(
state
.reasoning_effort_pin
.pin(&settings.model_info.slug, effort),
)
}
async fn effort_for_configuration_update(
&self,
settings: &ResolvedStepSettings,
+2 -1
View File
@@ -2313,7 +2313,8 @@ async fn try_run_sampling_request(
prompt,
&step_context.settings.model_info,
&step_context.session_telemetry,
step_context.settings.reasoning_effort().cloned(),
sess.reasoning_effort_for_request(&step_context.settings)
.await,
step_context.settings.reasoning_summary,
step_context.settings.service_tier.clone(),
responses_metadata,