mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
[codex] Allow empty turn/start requests (#23409)
## Why `turn/start` already accepts an input array on the wire, including an empty array, but core treated empty input as a no-op before the turn could reach the model. App-server clients need to be able to start a real turn even when there is no new user message, for example to let the model proceed from existing thread context. ## What changed - Removed the `run_turn` early return that skipped empty-input turns when there was no pending input. - Kept empty active-turn steering rejected by moving the `steer_input` empty-input check until after core has determined whether there is an active regular turn. - Empty regular turns now refresh `previous_turn_settings` like other regular turns, so follow-up context injection state advances consistently. - Added an app-server v2 integration test proving `turn/start` with `input: []` emits started/completed notifications, sends one Responses request, and does not synthesize an empty user message. ## Validation - `cargo test -p codex-app-server --test all turn_start_with_empty_input_runs_model_request`
This commit is contained in:
committed by
GitHub
Unverified
parent
b3ae3de405
commit
ef24ef127f
@@ -3143,10 +3143,6 @@ impl Session {
|
||||
expected_turn_id: Option<&str>,
|
||||
responsesapi_client_metadata: Option<HashMap<String, String>>,
|
||||
) -> Result<String, SteerInputError> {
|
||||
if input.is_empty() {
|
||||
return Err(SteerInputError::EmptyInput);
|
||||
}
|
||||
|
||||
let mut active = self.active_turn.lock().await;
|
||||
let Some(active_turn) = active.as_mut() else {
|
||||
return Err(SteerInputError::NoActiveTurn(input));
|
||||
@@ -3180,6 +3176,10 @@ impl Session {
|
||||
None => return Err(SteerInputError::NoActiveTurn(input)),
|
||||
}
|
||||
|
||||
if input.is_empty() {
|
||||
return Err(SteerInputError::EmptyInput);
|
||||
}
|
||||
|
||||
if let Some(responsesapi_client_metadata) = responsesapi_client_metadata
|
||||
&& let Some((_, active_task)) = active_turn.tasks.first()
|
||||
{
|
||||
|
||||
@@ -143,10 +143,6 @@ pub(crate) async fn run_turn(
|
||||
prewarmed_client_session: Option<ModelClientSession>,
|
||||
cancellation_token: CancellationToken,
|
||||
) -> Option<String> {
|
||||
if input.is_empty() && !sess.input_queue.has_pending_input(&sess.active_turn).await {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut client_session =
|
||||
prewarmed_client_session.unwrap_or_else(|| sess.services.model_client.new_session());
|
||||
// TODO(ccunningham): Pre-turn compaction runs before context updates and the
|
||||
@@ -210,16 +206,11 @@ pub(crate) async fn run_turn(
|
||||
sess.merge_connector_selection(explicitly_enabled_connectors.clone())
|
||||
.await;
|
||||
record_additional_contexts(&sess, &turn_context, additional_contexts).await;
|
||||
if !input.is_empty() {
|
||||
// Track the previous-turn baseline from the regular user-turn path only so
|
||||
// standalone tasks (compact/shell/review) cannot suppress future
|
||||
// model/realtime injections.
|
||||
sess.set_previous_turn_settings(Some(PreviousTurnSettings {
|
||||
model: turn_context.model_info.slug.clone(),
|
||||
realtime_active: Some(turn_context.realtime_active),
|
||||
}))
|
||||
.await;
|
||||
}
|
||||
sess.set_previous_turn_settings(Some(PreviousTurnSettings {
|
||||
model: turn_context.model_info.slug.clone(),
|
||||
realtime_active: Some(turn_context.realtime_active),
|
||||
}))
|
||||
.await;
|
||||
for response_item in injection_items {
|
||||
sess.record_conversation_items(&turn_context, std::slice::from_ref(&response_item))
|
||||
.await;
|
||||
|
||||
Reference in New Issue
Block a user