mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
[codex] Remove auto-compaction opt-out (#29815)
## Summary - remove the default-on `auto_compaction` feature flag and generated config schema entries - restore unconditional pre-turn, model-switch/hash, and mid-turn automatic compaction - expose `new_context` whenever token-budget tooling is enabled - remove the disabled-auto-compaction integration coverage introduced by #28260 ## Motivation Roll back the internal auto-compaction escape hatch added in #28260. Automatic compaction should no longer be suppressible with `--disable auto_compaction`; existing manual `/compact` behavior remains unchanged. ## Testing - `just write-config-schema` - `just test -p codex-features` — 53 passed - `just test -p codex-core 'suite::compact::'` — 36 passed - `just test -p codex-core suite::token_budget::new_context_tool_starts_new_window_before_follow_up` — 1 passed - `just fix -p codex-core -p codex-features` - `just fmt` - `just test -p codex-core` — 2,778 passed, 59 failed, 16 skipped; failures were outside the changed compaction paths and were dominated by missing first-party test binaries and shell-snapshot timeouts
This commit is contained in:
committed by
GitHub
Unverified
parent
31e428a1ef
commit
2a320fedb5
@@ -433,9 +433,6 @@
|
||||
"auth_elicitation": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"auto_compaction": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"browser_use": {
|
||||
"type": "boolean"
|
||||
},
|
||||
@@ -4744,9 +4741,6 @@
|
||||
"auth_elicitation": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"auto_compaction": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"browser_use": {
|
||||
"type": "boolean"
|
||||
},
|
||||
|
||||
@@ -332,13 +332,8 @@ pub(crate) async fn run_turn(
|
||||
.await;
|
||||
|
||||
// as long as compaction works well in getting us way below the token limit, we shouldn't worry about being in an infinite loop.
|
||||
let auto_compact_needed = turn_context
|
||||
.config
|
||||
.features
|
||||
.enabled(Feature::AutoCompaction)
|
||||
&& token_limit_reached;
|
||||
if needs_follow_up
|
||||
&& (sess.take_new_context_window_request().await || auto_compact_needed)
|
||||
&& (sess.take_new_context_window_request().await || token_limit_reached)
|
||||
{
|
||||
if let Err(err) = run_auto_compact(
|
||||
&sess,
|
||||
@@ -792,14 +787,6 @@ async fn run_pre_sampling_compact(
|
||||
turn_context: &Arc<TurnContext>,
|
||||
client_session: &mut ModelClientSession,
|
||||
) -> CodexResult<()> {
|
||||
if !turn_context
|
||||
.config
|
||||
.features
|
||||
.enabled(Feature::AutoCompaction)
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
maybe_run_previous_model_inline_compact(sess, turn_context, client_session).await?;
|
||||
let token_status =
|
||||
super::context_window::context_window_token_status(sess.as_ref(), turn_context.as_ref())
|
||||
|
||||
@@ -738,9 +738,7 @@ fn add_core_utility_tools(context: &CoreToolPlanContext<'_>, planned_tools: &mut
|
||||
}
|
||||
|
||||
if features.enabled(Feature::TokenBudget) {
|
||||
if features.enabled(Feature::AutoCompaction) {
|
||||
planned_tools.add_with_exposure(NewContextWindowHandler, ToolExposure::DirectModelOnly);
|
||||
}
|
||||
planned_tools.add_with_exposure(NewContextWindowHandler, ToolExposure::DirectModelOnly);
|
||||
planned_tools.add(GetContextRemainingHandler);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use anyhow::Result;
|
||||
use anyhow::anyhow;
|
||||
use codex_core::CodexThread;
|
||||
use codex_core::compact::SUMMARIZATION_PROMPT;
|
||||
use codex_core::compact::SUMMARY_PREFIX;
|
||||
use codex_core::config::Config;
|
||||
@@ -36,7 +35,6 @@ use core_test_support::responses;
|
||||
use core_test_support::responses::ev_reasoning_item;
|
||||
use core_test_support::responses::mount_models_once;
|
||||
use core_test_support::skip_if_no_network;
|
||||
use core_test_support::test_codex::TestCodex;
|
||||
use core_test_support::test_codex::local_selections;
|
||||
use core_test_support::test_codex::test_codex;
|
||||
use core_test_support::test_codex::turn_permission_fields;
|
||||
@@ -94,48 +92,6 @@ const REMOTE_V2_SUMMARY: &str = "global-instructions-remote-v2-summary";
|
||||
|
||||
pub(super) const COMPACT_WARNING_MESSAGE: &str = "Heads up: Long threads and multiple compactions can cause the model to be less accurate. Start a new thread when possible to keep threads small and targeted.";
|
||||
|
||||
async fn build_auto_compaction_disabled_codex(server: &MockServer) -> TestCodex {
|
||||
let mut model_provider = non_openai_model_provider(server);
|
||||
model_provider.stream_max_retries = Some(0);
|
||||
test_codex()
|
||||
.with_config(move |config| {
|
||||
config.model_provider = model_provider;
|
||||
set_test_compact_prompt(config);
|
||||
config.model_context_window = Some(100);
|
||||
config.model_auto_compact_token_limit = Some(90);
|
||||
let _ = config.features.disable(Feature::AutoCompaction);
|
||||
})
|
||||
.build(server)
|
||||
.await
|
||||
.expect("build codex")
|
||||
}
|
||||
|
||||
async fn submit_context_window_exceeded_turn(codex: &Arc<CodexThread>, text: &str) {
|
||||
codex
|
||||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: text.to_string(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
responsesapi_client_metadata: None,
|
||||
additional_context: Default::default(),
|
||||
thread_settings: Default::default(),
|
||||
})
|
||||
.await
|
||||
.expect("submit context window exceeded turn");
|
||||
let error_message = wait_for_event_match(codex, |event| match event {
|
||||
EventMsg::Error(err) => Some(err.message.clone()),
|
||||
_ => None,
|
||||
})
|
||||
.await;
|
||||
wait_for_event(codex, |event| matches!(event, EventMsg::TurnComplete(_))).await;
|
||||
assert!(
|
||||
error_message.contains("ran out of room in the model's context window"),
|
||||
"expected context window exceeded message, got {error_message}"
|
||||
);
|
||||
}
|
||||
|
||||
fn ev_shell_command_call(call_id: &str, command: &str) -> serde_json::Value {
|
||||
ev_function_call(
|
||||
call_id,
|
||||
@@ -2320,93 +2276,6 @@ async fn pre_sampling_compact_runs_when_comp_hash_changes() {
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn auto_compaction_feature_disabled_skips_comp_hash_model_switch_compaction() {
|
||||
skip_if_no_network!();
|
||||
|
||||
let server = MockServer::start().await;
|
||||
let previous_model = "gpt-5.3-codex";
|
||||
let next_model = "gpt-5.2";
|
||||
|
||||
let models_mock = mount_models_once(
|
||||
&server,
|
||||
ModelsResponse {
|
||||
models: vec![
|
||||
model_info_with_optional_comp_hash(previous_model, Some("hash-a")),
|
||||
model_info_with_optional_comp_hash(next_model, Some("hash-b")),
|
||||
],
|
||||
},
|
||||
)
|
||||
.await;
|
||||
let request_log = mount_sse_sequence(
|
||||
&server,
|
||||
vec![
|
||||
sse(vec![
|
||||
ev_assistant_message("m1", "before switch"),
|
||||
ev_completed_with_tokens("r1", /*total_tokens*/ 100),
|
||||
]),
|
||||
sse(vec![
|
||||
ev_assistant_message("m2", "after switch"),
|
||||
ev_completed_with_tokens("r2", /*total_tokens*/ 100),
|
||||
]),
|
||||
],
|
||||
)
|
||||
.await;
|
||||
let model_provider = non_openai_model_provider(&server);
|
||||
let mut builder = test_codex()
|
||||
.with_auth(CodexAuth::create_dummy_chatgpt_auth_for_testing())
|
||||
.with_model(previous_model)
|
||||
.with_config(move |config| {
|
||||
config.model_provider = model_provider;
|
||||
set_test_compact_prompt(config);
|
||||
let _ = config.features.disable(Feature::AutoCompaction);
|
||||
});
|
||||
let test = builder.build(&server).await.expect("build test codex");
|
||||
|
||||
test.codex
|
||||
.submit(disabled_permission_user_turn(
|
||||
"before switch",
|
||||
test.cwd.path().to_path_buf(),
|
||||
previous_model.to_string(),
|
||||
))
|
||||
.await
|
||||
.expect("submit first user turn");
|
||||
wait_for_event(&test.codex, |event| {
|
||||
matches!(event, EventMsg::TurnComplete(_))
|
||||
})
|
||||
.await;
|
||||
test.codex
|
||||
.submit(disabled_permission_user_turn(
|
||||
"after switch",
|
||||
test.cwd.path().to_path_buf(),
|
||||
next_model.to_string(),
|
||||
))
|
||||
.await
|
||||
.expect("submit second user turn");
|
||||
wait_for_event(&test.codex, |event| {
|
||||
matches!(event, EventMsg::TurnComplete(_))
|
||||
})
|
||||
.await;
|
||||
|
||||
let requests = request_log.requests();
|
||||
assert_eq!(models_mock.requests().len(), 1);
|
||||
assert_eq!(
|
||||
requests.len(),
|
||||
2,
|
||||
"disabled auto-compaction should skip compaction on a comp-hash model switch"
|
||||
);
|
||||
let first = requests[0].body_json();
|
||||
let second = requests[1].body_json();
|
||||
assert_eq!(first["model"].as_str(), Some(previous_model));
|
||||
assert_eq!(second["model"].as_str(), Some(next_model));
|
||||
assert!(second.to_string().contains("before switch"));
|
||||
assert!(second.to_string().contains("after switch"));
|
||||
assert!(
|
||||
!body_contains_text(&second.to_string(), SUMMARIZATION_PROMPT),
|
||||
"disabled auto-compaction should preserve history instead of requesting a summary"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn pre_sampling_compact_skips_when_either_comp_hash_is_missing() {
|
||||
skip_if_no_network!();
|
||||
@@ -3786,45 +3655,6 @@ async fn snapshot_request_shape_mid_turn_continuation_compaction() {
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn auto_compaction_feature_disabled_skips_mid_turn_compaction() {
|
||||
skip_if_no_network!();
|
||||
|
||||
let server = start_mock_server().await;
|
||||
let over_limit_tokens = 100 * 95 / 100 + 1;
|
||||
let first_turn = sse(vec![
|
||||
ev_function_call(DUMMY_CALL_ID, DUMMY_FUNCTION_NAME, "{}"),
|
||||
ev_completed_with_tokens("r1", over_limit_tokens),
|
||||
]);
|
||||
let request_log = mount_sse_sequence(
|
||||
&server,
|
||||
vec![
|
||||
first_turn,
|
||||
sse_failed(
|
||||
"response-failed",
|
||||
"context_length_exceeded",
|
||||
CONTEXT_LIMIT_MESSAGE,
|
||||
),
|
||||
],
|
||||
)
|
||||
.await;
|
||||
let test = build_auto_compaction_disabled_codex(&server).await;
|
||||
|
||||
submit_context_window_exceeded_turn(&test.codex, FUNCTION_CALL_LIMIT_MSG).await;
|
||||
|
||||
let requests = request_log.requests();
|
||||
assert_eq!(requests.len(), 2);
|
||||
let continuation_request = &requests[1];
|
||||
continuation_request.function_call_output(DUMMY_CALL_ID);
|
||||
assert!(
|
||||
!body_contains_text(
|
||||
&continuation_request.body_json().to_string(),
|
||||
SUMMARIZATION_PROMPT
|
||||
),
|
||||
"disabled auto-compaction should continue without a compaction request"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn auto_compact_clamps_config_limit_to_context_window() {
|
||||
skip_if_no_network!();
|
||||
@@ -4658,44 +4488,6 @@ async fn snapshot_request_shape_pre_turn_compaction_context_window_exceeded() {
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn auto_compaction_feature_disabled_skips_pre_turn_compaction() {
|
||||
skip_if_no_network!();
|
||||
|
||||
let server = start_mock_server().await;
|
||||
let first_turn = sse(vec![
|
||||
ev_assistant_message("m1", FIRST_REPLY),
|
||||
ev_completed_with_tokens("r1", /*total_tokens*/ 500),
|
||||
]);
|
||||
let request_log = mount_sse_sequence(
|
||||
&server,
|
||||
vec![
|
||||
first_turn,
|
||||
sse_failed(
|
||||
"response-failed",
|
||||
"context_length_exceeded",
|
||||
CONTEXT_LIMIT_MESSAGE,
|
||||
),
|
||||
],
|
||||
)
|
||||
.await;
|
||||
let test = build_auto_compaction_disabled_codex(&server).await;
|
||||
|
||||
test.submit_turn("USER_ONE")
|
||||
.await
|
||||
.expect("submit first turn");
|
||||
submit_context_window_exceeded_turn(&test.codex, "USER_TWO").await;
|
||||
|
||||
let requests = request_log.requests();
|
||||
assert_eq!(requests.len(), 2);
|
||||
let second_request_body = requests[1].body_json().to_string();
|
||||
assert!(second_request_body.contains("USER_TWO"));
|
||||
assert!(
|
||||
!body_contains_text(&second_request_body, SUMMARIZATION_PROMPT),
|
||||
"disabled auto-compaction should sample without a pre-turn compaction request"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn snapshot_request_shape_manual_compact_without_previous_user_messages() {
|
||||
skip_if_no_network!();
|
||||
|
||||
@@ -903,51 +903,3 @@ async fn new_context_tool_starts_new_window_before_follow_up() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn auto_compaction_feature_disabled_hides_new_context_tool() -> Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
|
||||
let server = start_mock_server().await;
|
||||
let responses = mount_sse_sequence(
|
||||
&server,
|
||||
vec![sse(vec![
|
||||
ev_response_created("resp-1"),
|
||||
ev_completed("resp-1"),
|
||||
])],
|
||||
)
|
||||
.await;
|
||||
let test = test_codex()
|
||||
.with_config(|config| {
|
||||
config.model_context_window = Some(CONFIGURED_CONTEXT_WINDOW);
|
||||
config
|
||||
.features
|
||||
.enable(Feature::TokenBudget)
|
||||
.expect("test config should allow token budget");
|
||||
config
|
||||
.features
|
||||
.disable(Feature::AutoCompaction)
|
||||
.expect("test config should allow disabling auto-compaction");
|
||||
})
|
||||
.build(&server)
|
||||
.await?;
|
||||
|
||||
test.submit_turn("preserve the current context window")
|
||||
.await?;
|
||||
|
||||
let requests = responses.requests();
|
||||
assert_eq!(requests.len(), 1);
|
||||
let tool_names = tool_names(&requests[0]);
|
||||
assert!(
|
||||
tool_names
|
||||
.iter()
|
||||
.any(|name| name == "get_context_remaining"),
|
||||
"token budget should continue to expose get_context_remaining"
|
||||
);
|
||||
assert!(
|
||||
!tool_names.iter().any(|name| name == "new_context"),
|
||||
"disabled auto-compaction should hide new_context"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -235,8 +235,6 @@ pub enum Feature {
|
||||
RealtimeConversation,
|
||||
/// Prevent idle system sleep while a turn is actively running.
|
||||
PreventIdleSleep,
|
||||
/// Enable automatic context compaction before or during a turn.
|
||||
AutoCompaction,
|
||||
/// Enable remote compaction v2 over the normal Responses API.
|
||||
RemoteCompactionV2,
|
||||
/// Use Agent Identity for ChatGPT-authenticated sessions.
|
||||
@@ -1347,12 +1345,6 @@ pub const FEATURES: &[FeatureSpec] = &[
|
||||
stage: Stage::Removed,
|
||||
default_enabled: false,
|
||||
},
|
||||
FeatureSpec {
|
||||
id: Feature::AutoCompaction,
|
||||
key: "auto_compaction",
|
||||
stage: Stage::Stable,
|
||||
default_enabled: true,
|
||||
},
|
||||
FeatureSpec {
|
||||
id: Feature::RemoteCompactionV2,
|
||||
key: "remote_compaction_v2",
|
||||
|
||||
Reference in New Issue
Block a user