mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
[codex] Defer steering until after sampling the model post-compaction (#17163)
## Summary - keep pending steered input buffered until the active user prompt has received a model response - keep steering pending across auto-compact when there is real model/tool continuation to resume - allow queued steering to follow compaction immediately when the prior model response was already final - keep pending-input follow-up owned by `run_turn` instead of folding it into `SamplingRequestResult` - add regression coverage for mid-turn compaction, final-response compaction, and compaction triggered before the next request after tool output ## Root Cause Steered input was drained at the top of every `run_turn` loop. After auto-compaction, the loop continued and immediately appended any pending steer after the compact summary, making a queued prompt look like the newest task instead of letting the model first resume interrupted model/tool work. ## Implementation Notes This patch keeps the follow-up signals separated: - `SamplingRequestResult.needs_follow_up` means model/tool continuation is needed - `sess.has_pending_input().await` means queued user steering exists - `run_turn` computes the combined loop condition from those two signals In `run_turn`: ```rust let has_pending_input = sess.has_pending_input().await; let needs_follow_up = model_needs_follow_up || has_pending_input; ``` After auto-compact we choose whether the next request may drain steering: ```rust can_drain_pending_input = !model_needs_follow_up; ``` That means: - model/tool continuation + pending steer: compact -> resume once without draining steer - completed model answer + pending steer: compact -> drain/send the steer immediately - fresh user prompt: do not drain steering before the model has answered the prompt once The drain is still only `sess.get_pending_input().await`; when `can_drain_pending_input` is false, core uses an empty local vec and leaves the steer pending in session state. ## Validation - PASS `cargo test -p codex-core --test all steered_user_input -- --nocapture` - PASS `just fmt` - PASS `git diff --check` - NOT PASSING HERE `just fix -p codex-core` currently stops before linting this change on an unrelated mainline test-build error: `core/src/tools/spec_tests.rs` initializes `ToolsConfigParams` without `image_generation_tool_auth_allowed`; this PR does not touch that file.
This commit is contained in:
committed by
GitHub
Unverified
parent
84a24fe333
commit
8f705b0702
@@ -6144,6 +6144,11 @@ pub(crate) async fn run_turn(
|
||||
// one instance across retries within this turn.
|
||||
let mut client_session =
|
||||
prewarmed_client_session.unwrap_or_else(|| sess.services.model_client.new_session());
|
||||
// Pending input is drained into history before building the next model request.
|
||||
// However, we defer that drain until after sampling in two cases:
|
||||
// 1. At the start of a turn, so the fresh user prompt in `input` gets sampled first.
|
||||
// 2. After auto-compact, when model/tool continuation needs to resume before any steer.
|
||||
let mut can_drain_pending_input = input.is_empty();
|
||||
|
||||
loop {
|
||||
if run_pending_session_start_hooks(&sess, &turn_context).await {
|
||||
@@ -6153,7 +6158,11 @@ pub(crate) async fn run_turn(
|
||||
// Note that pending_input would be something like a message the user
|
||||
// submitted through the UI while the model was running. Though the UI
|
||||
// may support this, the model might not.
|
||||
let pending_input = sess.get_pending_input().await;
|
||||
let pending_input = if can_drain_pending_input {
|
||||
sess.get_pending_input().await
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
let mut blocked_pending_input = false;
|
||||
let mut blocked_pending_input_contexts = Vec::new();
|
||||
@@ -6227,9 +6236,12 @@ pub(crate) async fn run_turn(
|
||||
{
|
||||
Ok(sampling_request_output) => {
|
||||
let SamplingRequestResult {
|
||||
needs_follow_up,
|
||||
needs_follow_up: model_needs_follow_up,
|
||||
last_agent_message: sampling_request_last_agent_message,
|
||||
} = sampling_request_output;
|
||||
can_drain_pending_input = true;
|
||||
let has_pending_input = sess.has_pending_input().await;
|
||||
let needs_follow_up = model_needs_follow_up || has_pending_input;
|
||||
let total_usage_tokens = sess.get_total_token_usage().await;
|
||||
let token_limit_reached = total_usage_tokens >= auto_compact_limit;
|
||||
|
||||
@@ -6242,6 +6254,8 @@ pub(crate) async fn run_turn(
|
||||
estimated_token_count = ?estimated_token_count,
|
||||
auto_compact_limit,
|
||||
token_limit_reached,
|
||||
model_needs_follow_up,
|
||||
has_pending_input,
|
||||
needs_follow_up,
|
||||
"post sampling token usage"
|
||||
);
|
||||
@@ -6259,6 +6273,7 @@ pub(crate) async fn run_turn(
|
||||
return None;
|
||||
}
|
||||
client_session.reset_websocket_session();
|
||||
can_drain_pending_input = !model_needs_follow_up;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -7767,8 +7782,6 @@ async fn try_run_sampling_request(
|
||||
.await;
|
||||
should_emit_turn_diff = true;
|
||||
|
||||
needs_follow_up |= sess.has_pending_input().await;
|
||||
|
||||
break Ok(SamplingRequestResult {
|
||||
needs_follow_up,
|
||||
last_agent_message,
|
||||
|
||||
Reference in New Issue
Block a user