mirror of
https://github.com/openai/codex.git
synced 2026-11-13 14:46:14 +00:00
Add durable user-message queue dispatch (#37204)
## What changed - Add a storage-neutral queue extension for listing, adding, editing, reordering, deleting, and explicitly starting queued user messages. - Dispatch queued messages in FIFO order when a thread becomes idle after a completed turn, while leaving them queued after interruptions or failures. - Wait for user input to be persisted before removing its queue item, and report hook rejection, task termination, and persistence failures distinctly. - Snapshot local image and audio attachments into validated data URLs before storing them, enforce input limits, and emit `ThreadQueueChanged` events after mutations. ## Testing - Cover queue mutations, automatic and explicit dispatch, interruption and failure behavior, hook rejection, invalid records, input limits, and local attachment snapshotting. GitOrigin-RevId: 14f39d7c8cbaccb767a6da84b82cced0edfcb4b9
This commit is contained in:
1 parent
70b4653232
commit
bc8b25ea02
42 files changed
+2114
-140
No files matched your search
@@ -73,7 +73,7 @@ async fn user_message_admission_reports_started_and_steered_for_concurrent_submi
|
||||
async move {
|
||||
barrier.wait().await;
|
||||
codex
|
||||
.submit_user_input_and_wait_for_admission(
|
||||
.submit_user_input_and_wait_for_persisted_admission(
|
||||
user_input("first message"),
|
||||
/*trace*/ None,
|
||||
Some("client-message-1".to_string()),
|
||||
@@ -87,7 +87,7 @@ async fn user_message_admission_reports_started_and_steered_for_concurrent_submi
|
||||
async move {
|
||||
barrier.wait().await;
|
||||
codex
|
||||
.submit_user_input_and_wait_for_admission(
|
||||
.submit_user_input_and_wait_for_persisted_admission(
|
||||
user_input("second message"),
|
||||
/*trace*/ None,
|
||||
Some("client-message-2".to_string()),
|
||||
@@ -97,11 +97,21 @@ async fn user_message_admission_reports_started_and_steered_for_concurrent_submi
|
||||
});
|
||||
barrier.wait().await;
|
||||
|
||||
timeout(
|
||||
Duration::from_secs(5),
|
||||
server.wait_for_request_count(/*count*/ 1),
|
||||
)
|
||||
.await
|
||||
.expect("the started turn should reach its first model request");
|
||||
release_response
|
||||
.send(())
|
||||
.expect("response gate should remain open");
|
||||
|
||||
let (first_admission, second_admission) = timeout(Duration::from_secs(5), async {
|
||||
tokio::join!(first_submission, second_submission)
|
||||
})
|
||||
.await
|
||||
.expect("both concurrent admissions should resolve before sampling finishes");
|
||||
.expect("both concurrent admissions should resolve once their messages are persisted");
|
||||
let first_admission = first_admission
|
||||
.expect("first submission task should finish")
|
||||
.expect("first user message should be admitted");
|
||||
@@ -125,9 +135,33 @@ async fn user_message_admission_reports_started_and_steered_for_concurrent_submi
|
||||
};
|
||||
assert_eq!(started_turn_id, steered_turn_id);
|
||||
|
||||
release_response
|
||||
.send(())
|
||||
.expect("response gate should remain open");
|
||||
let rollout_path = codex.rollout_path().expect("user-message rollout path");
|
||||
let rollout = tokio::fs::read_to_string(rollout_path)
|
||||
.await
|
||||
.expect("read persisted user-message rollout");
|
||||
let mut persisted_client_message_ids: Vec<_> = rollout
|
||||
.lines()
|
||||
.map(serde_json::from_str::<RolloutLine>)
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.expect("parse persisted user-message rollout")
|
||||
.into_iter()
|
||||
.filter_map(|line| match line.item {
|
||||
RolloutItem::EventMsg(EventMsg::ItemCompleted(event)) => match event.item {
|
||||
TurnItem::UserMessage(message) => message.client_id,
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
persisted_client_message_ids.sort();
|
||||
assert_eq!(
|
||||
persisted_client_message_ids,
|
||||
vec![
|
||||
"client-message-1".to_string(),
|
||||
"client-message-2".to_string()
|
||||
]
|
||||
);
|
||||
|
||||
let mut observed_client_message_ids = Vec::new();
|
||||
loop {
|
||||
let event = timeout(Duration::from_secs(10), codex.next_event())
|
||||
@@ -165,36 +199,6 @@ async fn user_message_admission_reports_started_and_steered_for_concurrent_submi
|
||||
assert!(request_bodies[1].to_string().contains("first message"));
|
||||
assert!(request_bodies[1].to_string().contains("second message"));
|
||||
|
||||
codex
|
||||
.flush_rollout()
|
||||
.await
|
||||
.expect("flush persisted user-message history");
|
||||
let rollout_path = codex.rollout_path().expect("user-message rollout path");
|
||||
let rollout = tokio::fs::read_to_string(rollout_path)
|
||||
.await
|
||||
.expect("read persisted user-message rollout");
|
||||
let mut persisted_client_message_ids: Vec<_> = rollout
|
||||
.lines()
|
||||
.map(serde_json::from_str::<RolloutLine>)
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.expect("parse persisted user-message rollout")
|
||||
.into_iter()
|
||||
.filter_map(|line| match line.item {
|
||||
RolloutItem::EventMsg(EventMsg::ItemCompleted(event)) => match event.item {
|
||||
TurnItem::UserMessage(message) => message.client_id,
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
persisted_client_message_ids.sort();
|
||||
assert_eq!(
|
||||
persisted_client_message_ids,
|
||||
vec![
|
||||
"client-message-1".to_string(),
|
||||
"client-message-2".to_string()
|
||||
]
|
||||
);
|
||||
server.shutdown().await;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user