Skip to content

Commit 75a9f45

Browse files
charley-oaiCodex
andcommitted
Track previous turn settings for realtime diffs
Replace previous_model with previous_turn_settings, read turn baselines from session state in initial and steady-state context updates, add remote compaction coverage for realtime transition reinjection, and fix synthetic TurnContextItem initializers for the new realtime_active field. Co-authored-by: Codex <noreply@openai.com>
1 parent 3b15b61 commit 75a9f45

17 files changed

+1060
-243
lines changed

codex-rs/app-server/tests/suite/send_message.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -628,6 +628,7 @@ fn append_rollout_turn_context(path: &Path, timestamp: &str, model: &str) -> std
628628
model: model.to_string(),
629629
personality: None,
630630
collaboration_mode: None,
631+
realtime_active: Some(false),
631632
effort: None,
632633
summary: ReasoningSummary::Auto,
633634
user_instructions: None,

codex-rs/core/src/codex.rs

Lines changed: 227 additions & 134 deletions
Large diffs are not rendered by default.

codex-rs/core/src/codex/rollout_reconstruction.rs

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use super::*;
55
#[derive(Debug)]
66
pub(super) struct RolloutReconstruction {
77
pub(super) history: Vec<ResponseItem>,
8-
pub(super) previous_model: Option<String>,
8+
pub(super) previous_turn_settings: Option<PreviousTurnSettings>,
99
pub(super) reference_context_item: Option<TurnContextItem>,
1010
}
1111

@@ -29,7 +29,7 @@ enum TurnReferenceContextItem {
2929
struct ActiveReplaySegment<'a> {
3030
turn_id: Option<String>,
3131
counts_as_user_turn: bool,
32-
previous_model: Option<String>,
32+
previous_turn_settings: Option<PreviousTurnSettings>,
3333
reference_context_item: TurnReferenceContextItem,
3434
base_replacement_history: Option<&'a [ResponseItem]>,
3535
}
@@ -42,7 +42,7 @@ fn turn_ids_are_compatible(active_turn_id: Option<&str>, item_turn_id: Option<&s
4242
fn finalize_active_segment<'a>(
4343
active_segment: ActiveReplaySegment<'a>,
4444
base_replacement_history: &mut Option<&'a [ResponseItem]>,
45-
previous_model: &mut Option<String>,
45+
previous_turn_settings: &mut Option<PreviousTurnSettings>,
4646
reference_context_item: &mut TurnReferenceContextItem,
4747
pending_rollback_turns: &mut usize,
4848
) {
@@ -64,9 +64,9 @@ fn finalize_active_segment<'a>(
6464
*base_replacement_history = Some(segment_base_replacement_history);
6565
}
6666

67-
// `previous_model` comes from the newest surviving user turn that established one.
68-
if previous_model.is_none() && active_segment.counts_as_user_turn {
69-
*previous_model = active_segment.previous_model;
67+
// `previous_turn_settings` come from the newest surviving user turn that established them.
68+
if previous_turn_settings.is_none() && active_segment.counts_as_user_turn {
69+
*previous_turn_settings = active_segment.previous_turn_settings;
7070
}
7171

7272
// `reference_context_item` comes from the newest surviving user turn baseline, or
@@ -94,7 +94,7 @@ impl Session {
9494
// are both known; then replay only the buffered surviving tail forward to preserve exact
9595
// history semantics.
9696
let mut base_replacement_history: Option<&[ResponseItem]> = None;
97-
let mut previous_model = None;
97+
let mut previous_turn_settings = None;
9898
let mut reference_context_item = TurnReferenceContextItem::NeverSet;
9999
// Rollback is "drop the newest N user turns". While scanning in reverse, that becomes
100100
// "skip the next N user-turn segments we finalize".
@@ -170,7 +170,10 @@ impl Session {
170170
active_segment.turn_id.as_deref(),
171171
ctx.turn_id.as_deref(),
172172
) {
173-
active_segment.previous_model = Some(ctx.model.clone());
173+
active_segment.previous_turn_settings = Some(PreviousTurnSettings {
174+
model: ctx.model.clone(),
175+
realtime_active: ctx.realtime_active,
176+
});
174177
if matches!(
175178
active_segment.reference_context_item,
176179
TurnReferenceContextItem::NeverSet
@@ -192,7 +195,7 @@ impl Session {
192195
finalize_active_segment(
193196
active_segment,
194197
&mut base_replacement_history,
195-
&mut previous_model,
198+
&mut previous_turn_settings,
196199
&mut reference_context_item,
197200
&mut pending_rollback_turns,
198201
);
@@ -204,7 +207,7 @@ impl Session {
204207
}
205208

206209
if base_replacement_history.is_some()
207-
&& previous_model.is_some()
210+
&& previous_turn_settings.is_some()
208211
&& !matches!(reference_context_item, TurnReferenceContextItem::NeverSet)
209212
{
210213
// At this point we have both eager resume metadata values and the replacement-
@@ -218,7 +221,7 @@ impl Session {
218221
finalize_active_segment(
219222
active_segment,
220223
&mut base_replacement_history,
221-
&mut previous_model,
224+
&mut previous_turn_settings,
222225
&mut reference_context_item,
223226
&mut pending_rollback_turns,
224227
);
@@ -287,7 +290,7 @@ impl Session {
287290

288291
RolloutReconstruction {
289292
history: history.raw_items().to_vec(),
290-
previous_model,
293+
previous_turn_settings,
291294
reference_context_item,
292295
}
293296
}

codex-rs/core/src/codex/rollout_reconstruction_tests.rs

Lines changed: 72 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,19 @@ fn assistant_message(text: &str) -> ResponseItem {
3333
}
3434
}
3535

36+
fn previous_turn_settings(
37+
model: impl Into<String>,
38+
realtime_active: Option<bool>,
39+
) -> PreviousTurnSettings {
40+
PreviousTurnSettings {
41+
model: model.into(),
42+
realtime_active,
43+
}
44+
}
45+
3646
#[tokio::test]
37-
async fn record_initial_history_resumed_bare_turn_context_does_not_hydrate_previous_model() {
47+
async fn record_initial_history_resumed_bare_turn_context_does_not_hydrate_previous_turn_settings()
48+
{
3849
let (session, turn_context) = make_session_and_context().await;
3950
let previous_model = "previous-rollout-model";
4051
let previous_context_item = TurnContextItem {
@@ -66,12 +77,12 @@ async fn record_initial_history_resumed_bare_turn_context_does_not_hydrate_previ
6677
}))
6778
.await;
6879

69-
assert_eq!(session.previous_model().await, None);
80+
assert_eq!(session.previous_turn_settings().await, None);
7081
assert!(session.reference_context_item().await.is_none());
7182
}
7283

7384
#[tokio::test]
74-
async fn record_initial_history_resumed_hydrates_previous_model_from_lifecycle_turn_with_missing_turn_context_id()
85+
async fn record_initial_history_resumed_hydrates_previous_turn_settings_from_lifecycle_turn_with_missing_turn_context_id()
7586
{
7687
let (session, turn_context) = make_session_and_context().await;
7788
let previous_model = "previous-rollout-model";
@@ -134,8 +145,11 @@ async fn record_initial_history_resumed_hydrates_previous_model_from_lifecycle_t
134145
.await;
135146

136147
assert_eq!(
137-
session.previous_model().await,
138-
Some(previous_model.to_string())
148+
session.previous_turn_settings().await,
149+
Some(previous_turn_settings(
150+
previous_model,
151+
Some(turn_context.realtime_active),
152+
))
139153
);
140154
}
141155

@@ -222,8 +236,11 @@ async fn reconstruct_history_rollback_keeps_history_and_metadata_in_sync_for_com
222236
vec![turn_one_user, turn_one_assistant]
223237
);
224238
assert_eq!(
225-
reconstructed.previous_model,
226-
Some(turn_context.model_info.slug.clone())
239+
reconstructed.previous_turn_settings,
240+
Some(previous_turn_settings(
241+
turn_context.model_info.slug.clone(),
242+
Some(turn_context.realtime_active),
243+
))
227244
);
228245
assert_eq!(
229246
serde_json::to_value(reconstructed.reference_context_item)
@@ -301,8 +318,11 @@ async fn reconstruct_history_rollback_keeps_history_and_metadata_in_sync_for_inc
301318
vec![turn_one_user, turn_one_assistant]
302319
);
303320
assert_eq!(
304-
reconstructed.previous_model,
305-
Some(turn_context.model_info.slug.clone())
321+
reconstructed.previous_turn_settings,
322+
Some(previous_turn_settings(
323+
turn_context.model_info.slug.clone(),
324+
Some(turn_context.realtime_active),
325+
))
306326
);
307327
assert_eq!(
308328
serde_json::to_value(reconstructed.reference_context_item)
@@ -404,8 +424,11 @@ async fn reconstruct_history_rollback_skips_non_user_turns_for_history_and_metad
404424
vec![turn_one_user, turn_one_assistant]
405425
);
406426
assert_eq!(
407-
reconstructed.previous_model,
408-
Some(turn_context.model_info.slug.clone())
427+
reconstructed.previous_turn_settings,
428+
Some(previous_turn_settings(
429+
turn_context.model_info.slug.clone(),
430+
Some(turn_context.realtime_active),
431+
))
409432
);
410433
assert_eq!(
411434
serde_json::to_value(reconstructed.reference_context_item)
@@ -458,7 +481,7 @@ async fn reconstruct_history_rollback_clears_history_and_metadata_when_exceeding
458481
.await;
459482

460483
assert_eq!(reconstructed.history, Vec::new());
461-
assert_eq!(reconstructed.previous_model, None);
484+
assert_eq!(reconstructed.previous_turn_settings, None);
462485
assert!(reconstructed.reference_context_item.is_none());
463486
}
464487

@@ -521,7 +544,7 @@ async fn record_initial_history_resumed_rollback_skips_only_user_turns() {
521544
}))
522545
.await;
523546

524-
assert_eq!(session.previous_model().await, None);
547+
assert_eq!(session.previous_turn_settings().await, None);
525548
assert!(session.reference_context_item().await.is_none());
526549
}
527550

@@ -591,8 +614,11 @@ async fn record_initial_history_resumed_rollback_drops_incomplete_user_turn_comp
591614
.await;
592615

593616
assert_eq!(
594-
session.previous_model().await,
595-
Some(turn_context.model_info.slug.clone())
617+
session.previous_turn_settings().await,
618+
Some(previous_turn_settings(
619+
turn_context.model_info.slug.clone(),
620+
Some(turn_context.realtime_active),
621+
))
596622
);
597623
assert_eq!(
598624
serde_json::to_value(session.reference_context_item().await)
@@ -639,7 +665,7 @@ async fn record_initial_history_resumed_does_not_seed_reference_context_item_aft
639665
}))
640666
.await;
641667

642-
assert_eq!(session.previous_model().await, None);
668+
assert_eq!(session.previous_turn_settings().await, None);
643669
assert!(session.reference_context_item().await.is_none());
644670
}
645671

@@ -783,8 +809,11 @@ async fn record_initial_history_resumed_turn_context_after_compaction_reestablis
783809
.await;
784810

785811
assert_eq!(
786-
session.previous_model().await,
787-
Some(previous_model.to_string())
812+
session.previous_turn_settings().await,
813+
Some(previous_turn_settings(
814+
previous_model,
815+
Some(turn_context.realtime_active),
816+
))
788817
);
789818
assert_eq!(
790819
serde_json::to_value(session.reference_context_item().await)
@@ -901,8 +930,11 @@ async fn record_initial_history_resumed_aborted_turn_without_id_clears_active_tu
901930
.await;
902931

903932
assert_eq!(
904-
session.previous_model().await,
905-
Some(previous_model.to_string())
933+
session.previous_turn_settings().await,
934+
Some(previous_turn_settings(
935+
previous_model,
936+
Some(turn_context.realtime_active),
937+
))
906938
);
907939
assert!(session.reference_context_item().await.is_none());
908940
}
@@ -1001,8 +1033,11 @@ async fn record_initial_history_resumed_unmatched_abort_preserves_active_turn_fo
10011033
.await;
10021034

10031035
assert_eq!(
1004-
session.previous_model().await,
1005-
Some(current_model.to_string())
1036+
session.previous_turn_settings().await,
1037+
Some(previous_turn_settings(
1038+
current_model,
1039+
Some(turn_context.realtime_active),
1040+
))
10061041
);
10071042
assert_eq!(
10081043
serde_json::to_value(session.reference_context_item().await)
@@ -1095,8 +1130,11 @@ async fn record_initial_history_resumed_trailing_incomplete_turn_compaction_clea
10951130
.await;
10961131

10971132
assert_eq!(
1098-
session.previous_model().await,
1099-
Some(previous_model.to_string())
1133+
session.previous_turn_settings().await,
1134+
Some(previous_turn_settings(
1135+
previous_model,
1136+
Some(turn_context.realtime_active),
1137+
))
11001138
);
11011139
assert!(session.reference_context_item().await.is_none());
11021140
}
@@ -1138,8 +1176,11 @@ async fn record_initial_history_resumed_trailing_incomplete_turn_preserves_turn_
11381176
.await;
11391177

11401178
assert_eq!(
1141-
session.previous_model().await,
1142-
Some(turn_context.model_info.slug.clone())
1179+
session.previous_turn_settings().await,
1180+
Some(previous_turn_settings(
1181+
turn_context.model_info.slug.clone(),
1182+
Some(turn_context.realtime_active),
1183+
))
11431184
);
11441185
assert_eq!(
11451186
serde_json::to_value(session.reference_context_item().await)
@@ -1242,8 +1283,11 @@ async fn record_initial_history_resumed_replaced_incomplete_compacted_turn_clear
12421283
.await;
12431284

12441285
assert_eq!(
1245-
session.previous_model().await,
1246-
Some(previous_model.to_string())
1286+
session.previous_turn_settings().await,
1287+
Some(previous_turn_settings(
1288+
previous_model,
1289+
Some(turn_context.realtime_active),
1290+
))
12471291
);
12481292
assert!(session.reference_context_item().await.is_none());
12491293
}

0 commit comments

Comments
 (0)