mirror of
https://github.com/openai/codex.git
synced 2026-11-15 06:51:36 +00:00
Record reasoning effort changes in conversation history behind a flag (#43110)
## What changed Add the disabled-by-default `reasoning_effort_override` feature for OpenAI models with `use_responses_lite` enabled. Append a trusted `configuration_update` after accepted input when no effort is established in surviving history or the resolved effort changes. Preserve the existing history prefix and continue sending the request-level reasoning effort. Share effort normalization through `ModelInfo::resolve_reasoning_effort` so requests and history updates resolve `ultra` consistently and translate `persistent` to `disabled`. Exclude other custom effort values from history updates, and compare only against harness-authored configuration items. ## Testing Add integration coverage for effort transitions, deduplication, history prefix and cache-key preservation, alias normalization, and feature/provider/model gating. Add unit coverage for model-specific `ultra` resolution, fallbacks, and `persistent` translation. GitOrigin-RevId: c3fe7050058076454d8ea20054a618bca9d48c7c
This commit is contained in:
1 parent
e67a8ae6c9
commit
56a8470aa0
11 files changed
+523
-39
No files matched your search
@@ -230,6 +230,7 @@ mod mcp_refresh;
|
||||
mod mcp_runtime;
|
||||
pub(crate) mod multi_agents;
|
||||
mod realtime_history;
|
||||
mod reasoning_effort;
|
||||
mod retained_context;
|
||||
mod review;
|
||||
mod rollout_budget;
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
//! Gated reasoning-effort updates appended after accepted input.
|
||||
//!
|
||||
//! Only trusted harness items establish an effort. Updates append to surviving
|
||||
//! history without replacing the request-level reasoning effort.
|
||||
|
||||
use super::session::Session;
|
||||
use super::step_context::StepContext;
|
||||
use super::step_settings::ResolvedStepSettings;
|
||||
use codex_features::Feature;
|
||||
use codex_history::CodexHarnessMetadata;
|
||||
use codex_history::ResponseItemEnvelope;
|
||||
use codex_protocol::models::ConfigurationReasoning;
|
||||
use codex_protocol::models::ResponseItem;
|
||||
use codex_protocol::openai_models::ReasoningEffort;
|
||||
|
||||
impl Session {
|
||||
/// Establishes the selected effort in surviving history, independent of replayed settings.
|
||||
pub(crate) async fn record_reasoning_effort_override(&self, step_context: &StepContext) {
|
||||
let settings = &step_context.settings;
|
||||
let Some(effort) = self.effort_for_configuration_update(settings).await else {
|
||||
return;
|
||||
};
|
||||
let should_skip = {
|
||||
let state = self.state.lock().await;
|
||||
let established_effort =
|
||||
state
|
||||
.history
|
||||
.annotated_items()
|
||||
.iter()
|
||||
.rev()
|
||||
.find_map(|envelope| {
|
||||
if !envelope
|
||||
.metadata
|
||||
.as_ref()
|
||||
.is_some_and(|metadata| metadata.harness_authored_configuration)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
match &envelope.item {
|
||||
ResponseItem::ConfigurationUpdate { reasoning } => {
|
||||
Some(&reasoning.effort)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
});
|
||||
established_effort == Some(&effort)
|
||||
};
|
||||
if should_skip {
|
||||
return;
|
||||
}
|
||||
|
||||
self.record_annotated_conversation_items(
|
||||
step_context.turn.as_ref(),
|
||||
vec![ResponseItemEnvelope {
|
||||
item: ResponseItem::ConfigurationUpdate {
|
||||
reasoning: ConfigurationReasoning { effort },
|
||||
},
|
||||
metadata: Some(CodexHarnessMetadata {
|
||||
harness_authored_configuration: true,
|
||||
..Default::default()
|
||||
}),
|
||||
}],
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
async fn effort_for_configuration_update(
|
||||
&self,
|
||||
settings: &ResolvedStepSettings,
|
||||
) -> Option<ReasoningEffort> {
|
||||
if !self.enabled(Feature::ReasoningEffortOverride)
|
||||
|| !settings.model_info.use_responses_lite
|
||||
|| !self.provider().await.is_openai()
|
||||
{
|
||||
return None;
|
||||
}
|
||||
let effort = settings
|
||||
.model_info
|
||||
.resolve_reasoning_effort(settings.effective_reasoning_effort()?);
|
||||
// Persistent normalizes to "disabled". Keep unknown custom values out of
|
||||
// durable updates so injected items stay bounded to known backend modes.
|
||||
if matches!(&effort, ReasoningEffort::Custom(value) if value != "disabled") {
|
||||
return None;
|
||||
}
|
||||
Some(effort)
|
||||
}
|
||||
}
|
||||
@@ -413,6 +413,10 @@ pub(crate) async fn run_turn(
|
||||
.record_step_world_state_if_changed(&world_state, step_context.as_ref())
|
||||
.await?;
|
||||
|
||||
// Keep the override after accepted input so ordinary turn rollback removes it too.
|
||||
sess.record_reasoning_effort_override(step_context.as_ref())
|
||||
.await;
|
||||
|
||||
// Construct the input that we will send to the model.
|
||||
let sampling_request_input: Vec<ResponseItem> = async {
|
||||
sess.clone_history()
|
||||
|
||||
Reference in new issue
Block a user