mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
core: reset context for token budget compaction (#29743)
## Why When `Feature::TokenBudget` is enabled, compaction should behave like `new_context`: start a fresh context window with the standard injected context, without asking the server to summarize old history and without carrying prior user or assistant messages into the next model request. This is still a compaction operation from the client lifecycle perspective. Manual `/compact` and auto-compaction should keep the same observable side effects that clients and hooks expect, including compact hooks and `TurnItem::ContextCompaction`. ## What changed - Added `compact_token_budget` to run token-budget manual and inline auto-compaction through a shared compaction lifecycle. - Split pending `new_context` requests from forced context-window startup: `take_new_context_window_request()` consumes pending requests, and `start_new_context_window()` installs a fresh context window. - Routed token-budget manual `/compact` and inline auto-compaction to install a fresh context window locally instead of calling server/local summarization. - Preserved compact lifecycle side effects for token-budget compaction by running pre/post compact hooks and emitting `ContextCompaction` item start/completion events. - Updated token-budget tests to assert fresh window IDs, absence of server-side compaction calls, dropped prior transcript messages/tool output after reset, and compact hook/item lifecycle behavior. ## Testing - `just test -p codex-core token_budget_context_uses_new_window_after_compaction` - `just test -p codex-core token_budget_compaction_runs_compact_hooks` - `just test -p codex-core token_budget_mid_turn_auto_compaction_resets_before_active_follow_up` --------- Co-authored-by: pakrym-oai <pakrym@openai.com>
This commit is contained in:
committed by
GitHub
Unverified
parent
3b4186986f
commit
32b65bbf7a
@@ -3425,16 +3425,21 @@ impl Session {
|
||||
state.request_new_context_window();
|
||||
}
|
||||
|
||||
pub(crate) async fn maybe_start_new_context_window(
|
||||
pub(crate) async fn take_new_context_window_request(&self) -> bool {
|
||||
let mut state = self.state.lock().await;
|
||||
state.take_new_context_window_request()
|
||||
}
|
||||
|
||||
pub(crate) async fn start_new_context_window(
|
||||
&self,
|
||||
turn_context: &TurnContext,
|
||||
world_state: Arc<WorldState>,
|
||||
) -> Option<u64> {
|
||||
) -> u64 {
|
||||
let window = {
|
||||
let mut state = self.state.lock().await;
|
||||
state.start_new_context_window_if_requested()
|
||||
state.start_new_context_window()
|
||||
};
|
||||
let (window_number, window_ids) = window?;
|
||||
let (window_number, window_ids) = window;
|
||||
let context_items = self
|
||||
.build_initial_context_with_world_state(turn_context, world_state.as_ref())
|
||||
.await;
|
||||
@@ -3462,7 +3467,7 @@ impl Session {
|
||||
state.queue_pending_session_start_source(codex_hooks::SessionStartSource::Compact);
|
||||
}
|
||||
self.recompute_token_usage(turn_context).await;
|
||||
Some(window_number)
|
||||
window_number
|
||||
}
|
||||
|
||||
pub(crate) async fn reference_context_item(&self) -> Option<TurnContextItem> {
|
||||
|
||||
@@ -331,22 +331,14 @@ pub(crate) async fn run_turn(
|
||||
)
|
||||
.await;
|
||||
|
||||
let started_new_context_window = sess
|
||||
.maybe_start_new_context_window(turn_context.as_ref(), Arc::clone(&world_state))
|
||||
.await
|
||||
.is_some();
|
||||
if started_new_context_window && needs_follow_up {
|
||||
can_drain_pending_input = !model_needs_follow_up;
|
||||
continue;
|
||||
}
|
||||
|
||||
// as long as compaction works well in getting us way below the token limit, we shouldn't worry about being in an infinite loop.
|
||||
if turn_context
|
||||
let auto_compact_needed = turn_context
|
||||
.config
|
||||
.features
|
||||
.enabled(Feature::AutoCompaction)
|
||||
&& token_limit_reached
|
||||
&& needs_follow_up
|
||||
&& token_limit_reached;
|
||||
if needs_follow_up
|
||||
&& (sess.take_new_context_window_request().await || auto_compact_needed)
|
||||
{
|
||||
if let Err(err) = run_auto_compact(
|
||||
&sess,
|
||||
@@ -928,6 +920,18 @@ async fn run_auto_compact(
|
||||
phase: CompactionPhase,
|
||||
) -> CodexResult<()> {
|
||||
let turn_context = &step_context.turn;
|
||||
if turn_context.config.features.enabled(Feature::TokenBudget) {
|
||||
// Compaction is the reset request, so force a new context window
|
||||
// instead of consuming a pending `new_context` tool request.
|
||||
crate::compact_token_budget::run_inline_auto_compact_task(
|
||||
Arc::clone(sess),
|
||||
Arc::clone(turn_context),
|
||||
initial_context_injection,
|
||||
)
|
||||
.await?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if should_use_remote_compact_task(turn_context.provider.info()) {
|
||||
if turn_context
|
||||
.config
|
||||
|
||||
Reference in New Issue
Block a user