mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
core: add UUIDv7 context window IDs (#28953)
## Why The token-budget context currently identifies a context window by its thread-local sequence number. A UUIDv7 gives the model a stable opaque identity that remains fixed for a window and rotates when compaction or `new_context` starts the next one. ## What changed - Preserve the existing monotonic value as `window_number` and add a UUIDv7 `window_id` to `CompactedItem`. - Generate and rotate the UUID with auto-compaction window state, persist it alongside the number, and reconstruct it on resume and rollback. - Accept legacy compacted rollout records where the numeric `window_id` represented the window number. - Use the UUID only in token-budget context; existing request headers and metadata continue using `thread_id:window_number`. ## Testing - `just test -p codex-protocol compacted_item::tests` - `just test -p codex-core token_budget`
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
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,
|
||||
window_id,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct SerializedCompactedItem {
|
||||
message: String,
|
||||
#[serde(default)]
|
||||
replacement_history: Option<Vec<ResponseItem>>,
|
||||
#[serde(default)]
|
||||
window_number: Option<u64>,
|
||||
#[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),
|
||||
window_id: Some("019b3f6e-7a10-7cc3-8b6e-1d09e2f7a001".to_string()),
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
serde_json::to_value(item)?,
|
||||
json!({
|
||||
"message": "summary",
|
||||
"window_number": 3,
|
||||
"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),
|
||||
window_id: None,
|
||||
}
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ pub use thread_id::ThreadId;
|
||||
pub use tool_name::ToolName;
|
||||
pub mod approvals;
|
||||
pub mod capabilities;
|
||||
mod compacted_item;
|
||||
pub mod config_types;
|
||||
pub mod dynamic_tools;
|
||||
pub mod error;
|
||||
|
||||
@@ -2971,13 +2971,17 @@ pub enum RolloutItem {
|
||||
EventMsg(EventMsg),
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, TS)]
|
||||
#[derive(Serialize, Clone, Debug, PartialEq, JsonSchema, TS)]
|
||||
pub struct CompactedItem {
|
||||
pub message: String,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub replacement_history: Option<Vec<ResponseItem>>,
|
||||
/// Monotonic position of this context window within the thread.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub window_id: Option<u64>,
|
||||
pub window_number: Option<u64>,
|
||||
/// UUIDv7 identity of this context window.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub window_id: Option<String>,
|
||||
}
|
||||
|
||||
impl From<CompactedItem> for ResponseItem {
|
||||
|
||||
Reference in New Issue
Block a user