mirror of
https://github.com/openai/codex.git
synced 2026-11-07 20:30:33 +00:00
Route response item IDs through a shared generator (#59312)
## What changed Introduce `ItemIdGenerator` and pass the runtime-owned generator through sessions, model clients, history, hooks, tool outputs, and memory requests. Keep UUIDv7 generation as the default and replace direct calls to `ResponseItemId::new` with the shared callable. Preserve existing item IDs and backfill missing or empty IDs through the same generator, including forked history before reconstruction and persistence. ## Testing Extend coverage for UUIDv7 IDs on user messages, agent messages, and additional tools, and assert that forked model context matches the backfilled items persisted to history. GitOrigin-RevId: 0dd9ccf0f46f0f1005a6acfc740133b03ce82259
This commit is contained in:
committed by
copyberry
parent
f2b94ef8b5
commit
d84c872464
@@ -0,0 +1,12 @@
|
||||
//! Response item ID generation shared by sessions and their consumers.
|
||||
|
||||
use crate::ResponseItemId;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Generates response item IDs from a type prefix for an owning session. Clones share the same callable.
|
||||
pub type ItemIdGenerator = Arc<dyn Fn(&str) -> ResponseItemId + Send + Sync>;
|
||||
|
||||
#[cfg(test)]
|
||||
pub(super) fn uuid_v7_item_id_generator_for_test() -> ItemIdGenerator {
|
||||
Arc::new(|prefix| ResponseItemId::with_suffix(prefix, uuid::Uuid::now_v7()))
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::AgentPath;
|
||||
use crate::ResponseItemId;
|
||||
use crate::ItemIdGenerator;
|
||||
use crate::ThreadId;
|
||||
use crate::dynamic_tools::DynamicToolCallOutputContentItem;
|
||||
use crate::mcp::CallToolResult;
|
||||
@@ -727,7 +727,10 @@ impl HookPromptFragment {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_hook_prompt_message(fragments: &[HookPromptFragment]) -> Option<ResponseItem> {
|
||||
pub fn build_hook_prompt_message(
|
||||
fragments: &[HookPromptFragment],
|
||||
item_id_generator: &ItemIdGenerator,
|
||||
) -> Option<ResponseItem> {
|
||||
let mut content = Vec::new();
|
||||
let mut metadata = Vec::new();
|
||||
for fragment in fragments {
|
||||
@@ -744,7 +747,7 @@ pub fn build_hook_prompt_message(fragments: &[HookPromptFragment]) -> Option<Res
|
||||
Some(ResponseItem::Message {
|
||||
status: None,
|
||||
encrypted_content: None,
|
||||
id: Some(ResponseItemId::new("msg")),
|
||||
id: Some(item_id_generator("msg")),
|
||||
role: "user".to_string(),
|
||||
content,
|
||||
phase: None,
|
||||
@@ -920,7 +923,9 @@ mod tests {
|
||||
HookPromptFragment::from_single_hook("Retry with care & joy.", "hook-run-1"),
|
||||
HookPromptFragment::from_single_hook("Then summarize cleanly.", "hook-run-2"),
|
||||
];
|
||||
let message = build_hook_prompt_message(&original).expect("hook prompt");
|
||||
let message =
|
||||
build_hook_prompt_message(&original, &crate::uuid_v7_item_id_generator_for_test())
|
||||
.expect("hook prompt");
|
||||
|
||||
let ResponseItem::Message { id, content, .. } = message else {
|
||||
panic!("expected hook prompt message");
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
pub mod account;
|
||||
mod agent_path;
|
||||
pub mod auth;
|
||||
mod item_id_generator;
|
||||
mod response_item_id;
|
||||
mod response_usage;
|
||||
mod sanitized_git_url;
|
||||
@@ -8,6 +9,9 @@ mod session_id;
|
||||
mod thread_id;
|
||||
mod tool_name;
|
||||
pub use agent_path::AgentPath;
|
||||
pub use item_id_generator::ItemIdGenerator;
|
||||
#[cfg(test)]
|
||||
use item_id_generator::uuid_v7_item_id_generator_for_test;
|
||||
pub use permission_profile_intersection::PermissionIntersectionError;
|
||||
pub use permission_profile_intersection::intersect_effective_permission_profiles;
|
||||
pub use response_item_id::ResponseItemId;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use super::ConfigurationReasoning;
|
||||
use crate::ResponseItemId;
|
||||
use crate::models::ResponseItem;
|
||||
use crate::openai_models::ReasoningEffort;
|
||||
use pretty_assertions::assert_eq;
|
||||
@@ -21,7 +20,9 @@ fn configuration_update_round_trips_without_message_fields() {
|
||||
}
|
||||
);
|
||||
|
||||
item.set_id(Some(ResponseItemId::new("configuration-id")));
|
||||
item.set_id(Some(crate::uuid_v7_item_id_generator_for_test()(
|
||||
"configuration-id",
|
||||
)));
|
||||
item.set_turn_id_if_missing("turn-id");
|
||||
item.set_create_time_if_missing(serde_json::Number::from(123));
|
||||
assert_eq!(
|
||||
|
||||
@@ -17,10 +17,6 @@ use ts_rs::TS;
|
||||
pub struct ResponseItemId(String);
|
||||
|
||||
impl ResponseItemId {
|
||||
pub fn new(prefix: &str) -> Self {
|
||||
Self::with_suffix(prefix, uuid::Uuid::now_v7())
|
||||
}
|
||||
|
||||
pub fn with_suffix(prefix: &str, suffix: impl fmt::Display) -> Self {
|
||||
Self(format!("{prefix}_{suffix}"))
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use super::ResponseItemId;
|
||||
|
||||
#[test]
|
||||
fn creates_prefixed_uuid_v7_ids() {
|
||||
let id = ResponseItemId::new("msg");
|
||||
let id = crate::uuid_v7_item_id_generator_for_test()("msg");
|
||||
let uuid = id.strip_prefix("msg_").expect("message prefix");
|
||||
assert_eq!(
|
||||
Uuid::parse_str(uuid).expect("UUID suffix").get_version(),
|
||||
|
||||
Reference in New Issue
Block a user