mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
d1209bddfc
## Why The rendered `<token_budget>` fragment identifies the thread and current context window, but it does not expose enough lineage to identify the first window in the thread or the immediately preceding window. Those IDs also need to remain stable across compaction, resume, and rollback. ## What changed - Track first, previous, and current UUIDv7 context-window IDs in auto-compaction state. - Render `thread_id`, `first_window_id`, `previous_window_id`, and the current window ID in the full `<token_budget>` fragment. - Persist the first and previous window IDs in compacted rollout checkpoints and restore them during rollout reconstruction. - Preserve compatibility with older compacted records that do not contain the new optional fields. - Update focused state, rendering, reconstruction, rollback, and serialization coverage. ## Validation - `just test -p codex-core token_budget` - `just test -p codex-protocol compacted_item::tests` - `just test -p codex-core tracks_prefill_and_window_boundaries` - `just test -p codex-core reconstruct_history_uses_replacement_history_verbatim` - `just test -p codex-core thread_rollback_restores_cleared_reference_context_item_after_compaction`
107 lines
3.3 KiB
Rust
107 lines
3.3 KiB
Rust
use crate::models::ResponseItem;
|
|
use crate::protocol::CompactedItem;
|
|
use serde::Deserialize;
|
|
|
|
// Before `window_number` was introduced, the numeric window number was serialized as
|
|
// `window_id`. Accept that shape so existing rollouts remain resumable.
|
|
impl<'de> Deserialize<'de> for CompactedItem {
|
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
where
|
|
D: serde::Deserializer<'de>,
|
|
{
|
|
let serialized = SerializedCompactedItem::deserialize(deserializer)?;
|
|
let mut window_number = serialized.window_number;
|
|
let window_id = match serialized.window_id {
|
|
Some(SerializedWindowId::Id(window_id)) => Some(window_id),
|
|
Some(SerializedWindowId::LegacyWindowNumber(legacy_window_number)) => {
|
|
window_number.get_or_insert(legacy_window_number);
|
|
None
|
|
}
|
|
None => None,
|
|
};
|
|
Ok(Self {
|
|
message: serialized.message,
|
|
replacement_history: serialized.replacement_history,
|
|
window_number,
|
|
first_window_id: serialized.first_window_id,
|
|
previous_window_id: serialized.previous_window_id,
|
|
window_id,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
struct SerializedCompactedItem {
|
|
message: String,
|
|
#[serde(default)]
|
|
replacement_history: Option<Vec<ResponseItem>>,
|
|
#[serde(default)]
|
|
window_number: Option<u64>,
|
|
#[serde(default)]
|
|
first_window_id: Option<String>,
|
|
#[serde(default)]
|
|
previous_window_id: Option<String>,
|
|
#[serde(default)]
|
|
window_id: Option<SerializedWindowId>,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(untagged)]
|
|
enum SerializedWindowId {
|
|
Id(String),
|
|
LegacyWindowNumber(u64),
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use anyhow::Result;
|
|
use pretty_assertions::assert_eq;
|
|
use serde_json::json;
|
|
|
|
#[test]
|
|
fn serializes_window_number_and_id() -> Result<()> {
|
|
let item = CompactedItem {
|
|
message: "summary".to_string(),
|
|
replacement_history: None,
|
|
window_number: Some(3),
|
|
first_window_id: Some("019b3f6e-0000-7000-8000-000000000001".to_string()),
|
|
previous_window_id: Some("019b3f6e-0000-7000-8000-000000000002".to_string()),
|
|
window_id: Some("019b3f6e-7a10-7cc3-8b6e-1d09e2f7a001".to_string()),
|
|
};
|
|
|
|
assert_eq!(
|
|
serde_json::to_value(item)?,
|
|
json!({
|
|
"message": "summary",
|
|
"window_number": 3,
|
|
"first_window_id": "019b3f6e-0000-7000-8000-000000000001",
|
|
"previous_window_id": "019b3f6e-0000-7000-8000-000000000002",
|
|
"window_id": "019b3f6e-7a10-7cc3-8b6e-1d09e2f7a001",
|
|
})
|
|
);
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn migrates_legacy_numeric_window_id() -> Result<()> {
|
|
let item = serde_json::from_value::<CompactedItem>(json!({
|
|
"message": "summary",
|
|
"window_id": 3,
|
|
}))?;
|
|
|
|
assert_eq!(
|
|
item,
|
|
CompactedItem {
|
|
message: "summary".to_string(),
|
|
replacement_history: None,
|
|
window_number: Some(3),
|
|
first_window_id: None,
|
|
previous_window_id: None,
|
|
window_id: None,
|
|
}
|
|
);
|
|
Ok(())
|
|
}
|
|
}
|