mirror of
https://github.com/openai/codex.git
synced 2026-10-28 18:58:46 +00:00
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:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -318,12 +318,15 @@ async fn schedule_startup_prewarm_inner(
|
||||
.await;
|
||||
let mut client_session = session.services.model_client.new_session();
|
||||
let websocket_warmup_started_at = Instant::now();
|
||||
// Prewarm establishes the request baseline before the first turn can change effort.
|
||||
client_session
|
||||
.prewarm_websocket(
|
||||
&startup_prompt,
|
||||
&step_context.settings.model_info,
|
||||
&step_context.session_telemetry,
|
||||
step_context.settings.reasoning_effort().cloned(),
|
||||
session
|
||||
.reasoning_effort_for_request(&step_context.settings)
|
||||
.await,
|
||||
step_context.settings.reasoning_summary,
|
||||
step_context.settings.service_tier.clone(),
|
||||
&responses_metadata,
|
||||
|
||||
@@ -9,6 +9,7 @@ pub(crate) use additional_context::AdditionalContextStore;
|
||||
pub(crate) use auto_compact_window::AutoCompactWindowIds;
|
||||
pub(crate) use auto_compact_window::AutoCompactWindowSnapshot;
|
||||
pub(crate) use service::SessionServices;
|
||||
pub(crate) use session::ReasoningEffortPin;
|
||||
pub(crate) use session::SessionState;
|
||||
pub(crate) use turn::AcceptedUserInputResponse;
|
||||
pub(crate) use turn::ActiveTurn;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
use codex_protocol::models::AdditionalPermissionProfile;
|
||||
use codex_protocol::models::BaseInstructionsProvenance;
|
||||
use codex_protocol::models::ResponseItem;
|
||||
use codex_protocol::openai_models::ReasoningEffort;
|
||||
use codex_sandboxing::policy_transforms::merge_permission_profiles;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
@@ -29,6 +30,38 @@ use codex_protocol::protocol::TurnContextItem;
|
||||
use codex_utils_output_truncation::TruncationPolicy;
|
||||
use tokio_util::task::AbortOnDropHandle;
|
||||
|
||||
/// Runtime request effort; startup and replay must re-establish the selected effort.
|
||||
pub(crate) enum ReasoningEffortPin {
|
||||
Unset,
|
||||
Active {
|
||||
model: String,
|
||||
effort: ReasoningEffort,
|
||||
},
|
||||
}
|
||||
|
||||
impl ReasoningEffortPin {
|
||||
pub(crate) fn get(&self, model: &str) -> Option<ReasoningEffort> {
|
||||
match self {
|
||||
Self::Active {
|
||||
model: pinned_model,
|
||||
effort,
|
||||
} if pinned_model == model => Some(effort.clone()),
|
||||
Self::Unset | Self::Active { .. } => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn pin(&mut self, model: &str, effort: ReasoningEffort) -> ReasoningEffort {
|
||||
if let Some(pinned) = self.get(model) {
|
||||
return pinned;
|
||||
}
|
||||
*self = Self::Active {
|
||||
model: model.to_owned(),
|
||||
effort: effort.clone(),
|
||||
};
|
||||
effort
|
||||
}
|
||||
}
|
||||
|
||||
/// Persistent, session-scoped state previously stored directly on `Session`.
|
||||
pub(crate) struct SessionState {
|
||||
pub(crate) session_configuration: SessionConfiguration,
|
||||
@@ -46,6 +79,8 @@ pub(crate) struct SessionState {
|
||||
previous_turn_settings: Option<PreviousTurnSettings>,
|
||||
/// Runtime accounting state for the active auto-compaction window.
|
||||
auto_compact_window: AutoCompactWindow,
|
||||
/// Original request effort for the current model while configuration updates remain active.
|
||||
pub(crate) reasoning_effort_pin: ReasoningEffortPin,
|
||||
/// Startup prewarmed session prepared during session initialization.
|
||||
pub(crate) startup_prewarm: Option<SessionStartupPrewarmHandle>,
|
||||
/// Retained after completion so later turns do not repeat speculative captures.
|
||||
@@ -84,6 +119,7 @@ impl SessionState {
|
||||
additional_context: AdditionalContextStore::default(),
|
||||
previous_turn_settings: None,
|
||||
auto_compact_window: AutoCompactWindow::new_with_ids(auto_compact_window_ids),
|
||||
reasoning_effort_pin: ReasoningEffortPin::Unset,
|
||||
startup_prewarm: None,
|
||||
shell_snapshot_prewarm: None,
|
||||
current_time_reminder: CurrentTimeReminderState::default(),
|
||||
|
||||
Reference in New Issue
Block a user