mirror of
https://github.com/openai/codex.git
synced 2026-10-18 17:18:52 +00:00
Render TUI prompts before submitting user turns (#33373)
## Why Starting a user turn can wait on remote work. Show the submitted prompt immediately instead of leaving the input invisible during that wait. ## What changed - Queue optimistic prompt history before app-event submissions and render a frame before starting the turn. - Preserve the existing submit-failure behavior for direct submissions. ## Testing - Verify that an initial prompt is added to history before its user-turn event is submitted during session replay. GitOrigin-RevId: 57f30c6e0067127e6b082a206f7d154fcfef8d7d
This commit is contained in:
@@ -411,6 +411,15 @@ impl App {
|
||||
return Ok(AppRunControl::Exit(ExitReason::Fatal(message)));
|
||||
}
|
||||
AppEvent::CodexOp(op) => {
|
||||
if matches!(&op, AppCommand::UserTurn { .. }) {
|
||||
self.handle_draw_pre_render(tui)?;
|
||||
if self.transcript_reflow.has_pending_reflow() {
|
||||
self.transcript_reflow.schedule_immediate();
|
||||
self.maybe_run_resize_reflow(tui)?;
|
||||
}
|
||||
self.chat_widget.pre_draw_tick();
|
||||
self.render_chat_widget_frame(tui)?;
|
||||
}
|
||||
self.chat_widget.prepare_local_op_submission(&op);
|
||||
self.submit_active_thread_op(app_server, op).await?;
|
||||
}
|
||||
|
||||
@@ -367,12 +367,14 @@ async fn enqueue_primary_thread_session_replays_turns_before_initial_prompt_subm
|
||||
.await?;
|
||||
|
||||
let mut saw_replayed_answer = false;
|
||||
let mut saw_optimistic_prompt = false;
|
||||
let mut submitted_items = None;
|
||||
while let Ok(event) = app_event_rx.try_recv() {
|
||||
match event {
|
||||
AppEvent::InsertHistoryCell(cell) => {
|
||||
let transcript = lines_to_single_string(&cell.transcript_lines(/*width*/ 80));
|
||||
saw_replayed_answer |= transcript.contains("earlier prompt");
|
||||
saw_optimistic_prompt |= transcript.contains(&initial_prompt);
|
||||
}
|
||||
AppEvent::SubmitThreadOp {
|
||||
thread_id: op_thread_id,
|
||||
@@ -382,6 +384,10 @@ async fn enqueue_primary_thread_session_replays_turns_before_initial_prompt_subm
|
||||
submitted_items = Some(items);
|
||||
}
|
||||
AppEvent::CodexOp(Op::UserTurn { items, .. }) => {
|
||||
assert!(
|
||||
saw_optimistic_prompt,
|
||||
"expected optimistic prompt before turn submission"
|
||||
);
|
||||
submitted_items = Some(items);
|
||||
}
|
||||
_ => {}
|
||||
|
||||
@@ -350,6 +350,25 @@ impl ChatWidget {
|
||||
collaboration_mode,
|
||||
personality,
|
||||
);
|
||||
let submitted_message = UserMessage {
|
||||
text,
|
||||
local_images,
|
||||
remote_image_urls,
|
||||
text_elements,
|
||||
mention_bindings,
|
||||
};
|
||||
|
||||
// App-event submissions are handled serially, and turn/start can wait on remote work.
|
||||
// Queue the optimistic prompt first so the user's input is visible while that happens.
|
||||
// Direct submissions do not share that queue, so keep their existing failure behavior.
|
||||
let render_before_submit =
|
||||
render_in_history && matches!(&self.codex_op_target, CodexOpTarget::AppEvent);
|
||||
if render_before_submit {
|
||||
self.on_user_message_display(user_message_display_for_history(
|
||||
submitted_message.clone(),
|
||||
&history_record,
|
||||
));
|
||||
}
|
||||
|
||||
if !self.submit_op(op.clone()) {
|
||||
return (false, None);
|
||||
@@ -360,7 +379,8 @@ impl ChatWidget {
|
||||
|
||||
// Persist the submitted text to cross-session message history. Mentions are encoded into
|
||||
// placeholder syntax so recall can reconstruct the mention bindings in a future session.
|
||||
let encoded_mentions = mention_bindings
|
||||
let encoded_mentions = submitted_message
|
||||
.mention_bindings
|
||||
.iter()
|
||||
.map(|binding| LinkedMention {
|
||||
sigil: binding.sigil,
|
||||
@@ -369,8 +389,11 @@ impl ChatWidget {
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let history_text = match &history_record {
|
||||
UserMessageHistoryRecord::UserMessageText if !text.is_empty() => {
|
||||
Some(encode_history_mentions(&text, &encoded_mentions))
|
||||
UserMessageHistoryRecord::UserMessageText if !submitted_message.text.is_empty() => {
|
||||
Some(encode_history_mentions(
|
||||
&submitted_message.text,
|
||||
&encoded_mentions,
|
||||
))
|
||||
}
|
||||
UserMessageHistoryRecord::Override(history) if !history.text.is_empty() => {
|
||||
Some(encode_history_mentions(&history.text, &encoded_mentions))
|
||||
@@ -390,30 +413,13 @@ impl ChatWidget {
|
||||
}
|
||||
|
||||
if render_in_history {
|
||||
self.safety_buffering_prompt = Some(UserMessage {
|
||||
text: text.clone(),
|
||||
local_images: local_images.clone(),
|
||||
remote_image_urls: remote_image_urls.clone(),
|
||||
text_elements: text_elements.clone(),
|
||||
mention_bindings: mention_bindings.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
// Show replayable user content in conversation history.
|
||||
let display_user_message = render_in_history.then(|| {
|
||||
user_message_display_for_history(
|
||||
UserMessage {
|
||||
text,
|
||||
local_images,
|
||||
remote_image_urls,
|
||||
text_elements,
|
||||
mention_bindings,
|
||||
},
|
||||
&history_record,
|
||||
)
|
||||
});
|
||||
if let Some(display) = display_user_message {
|
||||
self.on_user_message_display(display);
|
||||
self.safety_buffering_prompt = Some(submitted_message.clone());
|
||||
if !render_before_submit {
|
||||
self.on_user_message_display(user_message_display_for_history(
|
||||
submitted_message,
|
||||
&history_record,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
self.transcript.needs_final_message_separator = false;
|
||||
|
||||
Reference in New Issue
Block a user