mirror of
https://github.com/openai/codex.git
synced 2026-04-24 06:35:50 +00:00
core: collapse reference turn context state
Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
@@ -3664,9 +3664,9 @@ impl Session {
|
||||
self.persist_rollout_items(&[RolloutItem::TurnContext(turn_context_item.clone())])
|
||||
.await;
|
||||
|
||||
// Advance the in-memory turn-context tracker even when this turn emitted no model-visible
|
||||
// context items. Regular turns become both the latest turn-settings source and the active
|
||||
// model-visible reference baseline for subsequent diffing.
|
||||
// Advance the stored turn-context snapshot even when this turn emitted no model-visible
|
||||
// context items. Regular turns become both the `previous_turn_settings()` source and the
|
||||
// active model-visible reference baseline for subsequent diffing.
|
||||
self.record_regular_turn_context(turn_context_item).await;
|
||||
}
|
||||
|
||||
|
||||
@@ -65,38 +65,29 @@ fn merge_surviving_segment_turn_context_state(
|
||||
segment_turn_context_state: ReferenceTurnContextState,
|
||||
counts_as_user_turn: bool,
|
||||
) {
|
||||
// Only real user turns should backfill "previous turn settings". Standalone task turns may
|
||||
// carry lifecycle events, but they must not become the latest real turn context.
|
||||
if counts_as_user_turn
|
||||
&& reference_turn_context_state
|
||||
.latest_turn_context_item()
|
||||
.is_none()
|
||||
&& let Some(turn_context_item) = segment_turn_context_state.latest_turn_context_item()
|
||||
{
|
||||
reference_turn_context_state.set_latest_turn_context_item(Some(turn_context_item));
|
||||
}
|
||||
|
||||
// A compaction seen in this segment hides older reference baselines, but it must not erase a
|
||||
// newer stored reference baseline we already captured from a later surviving user turn.
|
||||
// A compaction seen in this segment shadows any older stored turn context, but it must not
|
||||
// erase a newer stored turn context we already captured from a later surviving user turn.
|
||||
if segment_turn_context_state.compacted_since_model_saw_reference_turn_context()
|
||||
&& reference_turn_context_state
|
||||
.stored_reference_turn_context_item()
|
||||
.stored_turn_context_item()
|
||||
.is_none()
|
||||
{
|
||||
reference_turn_context_state.note_compaction();
|
||||
}
|
||||
|
||||
// The model-visible reference baseline comes from the newest surviving user turn that both
|
||||
// carries a stored baseline and has not been hidden by a later surviving compaction.
|
||||
// Only real user turns should establish the stored turn context. Standalone task turns may
|
||||
// carry lifecycle events, but they must not become the source of `previous_turn_settings()`
|
||||
// or the reference baseline.
|
||||
//
|
||||
// This stores the newest surviving real turn context, while preserving any later compaction
|
||||
// shadow we already learned about from newer surviving segments.
|
||||
if counts_as_user_turn
|
||||
&& !reference_turn_context_state.compacted_since_model_saw_reference_turn_context()
|
||||
&& reference_turn_context_state
|
||||
.stored_reference_turn_context_item()
|
||||
.stored_turn_context_item()
|
||||
.is_none()
|
||||
&& let Some(turn_context_item) =
|
||||
segment_turn_context_state.stored_reference_turn_context_item()
|
||||
&& let Some(turn_context_item) = segment_turn_context_state.stored_turn_context_item()
|
||||
{
|
||||
reference_turn_context_state.set_reference_context_item(Some(turn_context_item));
|
||||
reference_turn_context_state.note_turn_context_during_reverse_replay(&turn_context_item);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,7 +208,7 @@ impl Session {
|
||||
if base_replacement_history.is_some()
|
||||
&& pending_rollback_turns == 0
|
||||
&& reference_turn_context_state
|
||||
.latest_turn_context_item()
|
||||
.stored_turn_context_item()
|
||||
.is_some()
|
||||
{
|
||||
// At this point the replay-derived metadata and replacement-history base for the
|
||||
|
||||
@@ -271,7 +271,7 @@ async fn reconstruct_history_rollback_keeps_history_and_metadata_in_sync_for_com
|
||||
serde_json::to_value(
|
||||
reconstructed
|
||||
.reference_turn_context_state
|
||||
.latest_turn_context_item(),
|
||||
.stored_turn_context_item(),
|
||||
)
|
||||
.expect("serialize surviving turn context item"),
|
||||
serde_json::to_value(Some(first_context_item))
|
||||
@@ -499,7 +499,7 @@ async fn reconstruct_history_rollback_backfills_surviving_turn_context_from_olde
|
||||
serde_json::to_value(
|
||||
reconstructed
|
||||
.reference_turn_context_state
|
||||
.latest_turn_context_item(),
|
||||
.stored_turn_context_item(),
|
||||
)
|
||||
.expect("serialize surviving turn context item"),
|
||||
serde_json::to_value(Some(first_context_item))
|
||||
|
||||
@@ -1299,7 +1299,7 @@ async fn fork_startup_context_then_first_turn_diff_snapshot() -> anyhow::Result<
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn record_initial_history_forked_hydrates_previous_turn_settings() {
|
||||
async fn record_initial_history_forked_advances_previous_turn_settings_to_current_baseline() {
|
||||
let (session, turn_context) = make_session_and_context().await;
|
||||
let previous_model = "forked-rollout-model";
|
||||
let previous_context_item = TurnContextItem {
|
||||
@@ -1358,7 +1358,7 @@ async fn record_initial_history_forked_hydrates_previous_turn_settings() {
|
||||
assert_eq!(
|
||||
session.previous_turn_settings().await,
|
||||
Some(PreviousTurnSettings {
|
||||
model: previous_model.to_string(),
|
||||
model: turn_context.model_info.slug.clone(),
|
||||
realtime_active: Some(turn_context.realtime_active),
|
||||
})
|
||||
);
|
||||
@@ -4119,7 +4119,7 @@ async fn record_context_updates_and_set_reference_context_item_reinjects_full_co
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn record_context_updates_and_set_reference_context_item_persists_baseline_without_emitting_diffs()
|
||||
async fn record_context_updates_and_set_reference_context_item_emits_model_switch_for_stored_baseline()
|
||||
{
|
||||
let (session, previous_context) = make_session_and_context().await;
|
||||
let next_model = if previous_context.model_info.slug == "gpt-5.1" {
|
||||
@@ -4160,7 +4160,7 @@ async fn record_context_updates_and_set_reference_context_item_persists_baseline
|
||||
let update_items = session
|
||||
.build_settings_update_items(Some(&previous_context_item), &turn_context)
|
||||
.await;
|
||||
assert_eq!(update_items, Vec::new());
|
||||
assert!(!update_items.is_empty());
|
||||
|
||||
session
|
||||
.record_context_updates_and_set_reference_context_item(&turn_context)
|
||||
@@ -4168,7 +4168,7 @@ async fn record_context_updates_and_set_reference_context_item_persists_baseline
|
||||
|
||||
assert_eq!(
|
||||
session.clone_history().await.raw_items().to_vec(),
|
||||
Vec::new()
|
||||
update_items
|
||||
);
|
||||
assert_eq!(
|
||||
serde_json::to_value(session.reference_context_item().await)
|
||||
|
||||
@@ -39,30 +39,26 @@ pub(crate) struct ContextManager {
|
||||
reference_turn_context_state: ReferenceTurnContextState,
|
||||
}
|
||||
|
||||
/// Session-owned bookkeeping for turn-context state that survives history replay,
|
||||
/// rollback, and compaction.
|
||||
/// Session-owned bookkeeping for the stored turn context that drives both
|
||||
/// `previous_turn_settings()` and the model-visible reference baseline.
|
||||
///
|
||||
/// This intentionally tracks both the latest real turn context we know about and the
|
||||
/// model-visible reference baseline, because those diverge when compaction hides the
|
||||
/// baseline without erasing the last real turn's settings.
|
||||
/// The stored turn context survives compaction so future turns can still diff
|
||||
/// against the last known settings, while
|
||||
/// `compacted_since_model_saw_reference_turn_context` controls whether the model
|
||||
/// may still treat that stored item as the active reference baseline.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub(crate) struct ReferenceTurnContextState {
|
||||
/// The most recent real turn context we reconstructed or recorded, even if a later
|
||||
/// compaction means the model can no longer rely on it as the active baseline.
|
||||
/// The last stored turn context we reconstructed or recorded.
|
||||
///
|
||||
/// This drives `previous_turn_settings()` and other rollback bookkeeping, which
|
||||
/// intentionally survive compaction until a newer real turn replaces them.
|
||||
latest_turn_context_item: Option<TurnContextItem>,
|
||||
/// The last turn context item that established the model's reference baseline.
|
||||
///
|
||||
/// Unlike `latest_turn_context_item`, this is only model-visible when
|
||||
/// `compacted_since_model_saw_reference_turn_context` is false.
|
||||
reference_turn_context_item: Option<TurnContextItem>,
|
||||
/// This is the single source of truth for both `previous_turn_settings()` and
|
||||
/// the reference baseline. Compaction can shadow it for model visibility
|
||||
/// without erasing it.
|
||||
turn_context_item: Option<TurnContextItem>,
|
||||
/// Whether compaction has crossed the current reference baseline without a later
|
||||
/// reinjection or real turn context re-establishing it.
|
||||
///
|
||||
/// When this is true, `reference_context_item()` must return `None` even if
|
||||
/// `reference_turn_context_item` still retains the last stored baseline for replay or
|
||||
/// `turn_context_item` still retains the last stored baseline for replay or
|
||||
/// rollback bookkeeping.
|
||||
compacted_since_model_saw_reference_turn_context: bool,
|
||||
}
|
||||
@@ -77,7 +73,7 @@ impl ReferenceTurnContextState {
|
||||
}
|
||||
|
||||
pub(crate) fn note_compaction_during_reverse_replay(&mut self) {
|
||||
if self.reference_turn_context_item.is_none() {
|
||||
if self.turn_context_item.is_none() {
|
||||
self.compacted_since_model_saw_reference_turn_context = true;
|
||||
}
|
||||
}
|
||||
@@ -86,39 +82,27 @@ impl ReferenceTurnContextState {
|
||||
&mut self,
|
||||
turn_context_item: &TurnContextItem,
|
||||
) {
|
||||
if self.latest_turn_context_item.is_none() {
|
||||
self.latest_turn_context_item = Some(turn_context_item.clone());
|
||||
if self.turn_context_item.is_none() {
|
||||
self.turn_context_item = Some(turn_context_item.clone());
|
||||
}
|
||||
if self.reference_turn_context_item.is_none() {
|
||||
self.reference_turn_context_item = Some(turn_context_item.clone());
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_latest_turn_context_item(&mut self, item: Option<TurnContextItem>) {
|
||||
self.latest_turn_context_item = item;
|
||||
}
|
||||
|
||||
pub(crate) fn record_regular_turn_context(&mut self, turn_context_item: TurnContextItem) {
|
||||
self.latest_turn_context_item = Some(turn_context_item.clone());
|
||||
self.reference_turn_context_item = Some(turn_context_item);
|
||||
self.turn_context_item = Some(turn_context_item);
|
||||
self.compacted_since_model_saw_reference_turn_context = false;
|
||||
}
|
||||
|
||||
pub(crate) fn set_reference_context_item(&mut self, item: Option<TurnContextItem>) {
|
||||
if let Some(item) = item {
|
||||
self.reference_turn_context_item = Some(item);
|
||||
self.turn_context_item = Some(item);
|
||||
self.compacted_since_model_saw_reference_turn_context = false;
|
||||
} else {
|
||||
self.note_compaction();
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn latest_turn_context_item(&self) -> Option<TurnContextItem> {
|
||||
self.latest_turn_context_item.clone()
|
||||
}
|
||||
|
||||
pub(crate) fn stored_reference_turn_context_item(&self) -> Option<TurnContextItem> {
|
||||
self.reference_turn_context_item.clone()
|
||||
pub(crate) fn stored_turn_context_item(&self) -> Option<TurnContextItem> {
|
||||
self.turn_context_item.clone()
|
||||
}
|
||||
|
||||
pub(crate) fn compacted_since_model_saw_reference_turn_context(&self) -> bool {
|
||||
@@ -126,7 +110,7 @@ impl ReferenceTurnContextState {
|
||||
}
|
||||
|
||||
pub(crate) fn previous_turn_settings(&self) -> Option<PreviousTurnSettings> {
|
||||
self.latest_turn_context_item
|
||||
self.turn_context_item
|
||||
.as_ref()
|
||||
.map(|turn_context_item| PreviousTurnSettings {
|
||||
model: turn_context_item.model.clone(),
|
||||
@@ -138,7 +122,7 @@ impl ReferenceTurnContextState {
|
||||
if self.compacted_since_model_saw_reference_turn_context {
|
||||
None
|
||||
} else {
|
||||
self.reference_turn_context_item.clone()
|
||||
self.turn_context_item.clone()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user