mirror of
https://github.com/openai/codex.git
synced 2026-10-20 17:38:45 +00:00
Record local image inputs on completed image-generation items (#54420)
## What changed Add optional `inputImages` metadata containing each referenced local image's path and SHA-256 hash of the bytes read before normalization. Compute the hash from the same bytes used to prepare the edit request. Preserve this metadata through completion events, persisted thread history, resume, and legacy rollout migration. Update app-server schemas and generated TypeScript and Python types; older items continue to deserialize without the field. ## Testing Add coverage for input ordering, changed contents at the same path, unreadable or invalid references, serialization, persisted history and resume, and legacy migration. GitOrigin-RevId: 522be8ec1e39c9b2bbe5b9ccb37ea67d17f0e21a
This commit is contained in:
@@ -221,6 +221,7 @@ pub(super) fn completed_item(
|
||||
transparent_background: event.transparent_background,
|
||||
failure: event.failure.clone(),
|
||||
saved_path: event.saved_path.clone(),
|
||||
input_images: event.input_images.clone(),
|
||||
imagegen_request_id: None,
|
||||
generation_id: None,
|
||||
})),
|
||||
|
||||
@@ -11,6 +11,7 @@ use codex_app_server_protocol::build_turns_from_rollout_items;
|
||||
use codex_extension_items::ExtensionItem;
|
||||
use codex_extension_items::image_generation::GeneratedImageRef;
|
||||
use codex_extension_items::image_generation::ImageGenerationFailure;
|
||||
use codex_extension_items::image_generation::ImageGenerationInputImage;
|
||||
use codex_extension_items::image_generation::ImageGenerationItem;
|
||||
use codex_protocol::AgentPath;
|
||||
use codex_protocol::ResponseItemId;
|
||||
@@ -50,6 +51,7 @@ use codex_rollout::RetainedContextEvent;
|
||||
use codex_rollout::RolloutConfig;
|
||||
use codex_rollout::RolloutItem;
|
||||
use codex_rollout::RolloutLine;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use pretty_assertions::assert_eq;
|
||||
use serde_json::json;
|
||||
use tempfile::TempDir;
|
||||
@@ -542,6 +544,7 @@ async fn migration_preserves_image_generation_failure_metadata() {
|
||||
resets_at: Some(1_786_150_800),
|
||||
}),
|
||||
saved_path: None,
|
||||
input_images: Vec::new(),
|
||||
imagegen_request_id: None,
|
||||
generation_id: None,
|
||||
};
|
||||
@@ -554,6 +557,70 @@ async fn migration_preserves_image_generation_failure_metadata() {
|
||||
transparent_background: expected_item.transparent_background,
|
||||
failure: expected_item.failure.clone(),
|
||||
saved_path: expected_item.saved_path.clone(),
|
||||
input_images: Vec::new(),
|
||||
}));
|
||||
let path = write_rollout(
|
||||
home.path(),
|
||||
thread_id,
|
||||
SessionSource::Cli,
|
||||
vec![
|
||||
started("image-turn"),
|
||||
image_completion,
|
||||
completed("image-turn"),
|
||||
],
|
||||
);
|
||||
let store = indexed_store(home.path()).await;
|
||||
|
||||
store
|
||||
.migrate_rollouts(apply_options())
|
||||
.await
|
||||
.expect("migrate legacy image completion");
|
||||
|
||||
let migrated_item = read_rollout(&path)
|
||||
.into_iter()
|
||||
.find_map(|line| match line.item {
|
||||
RolloutItem::EventMsg(EventMsg::ItemCompleted(event)) => Some(event.item),
|
||||
_ => None,
|
||||
})
|
||||
.expect("migrated image completion");
|
||||
let TurnItem::Extension(ExtensionItem::ImageGeneration(migrated_item)) = migrated_item else {
|
||||
panic!("expected migrated extension image-generation item");
|
||||
};
|
||||
assert_eq!(migrated_item, expected_item);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn migration_preserves_image_generation_input_observations() {
|
||||
let home = TempDir::new().expect("create Codex home");
|
||||
let thread_id = ThreadId::new();
|
||||
let expected_item = ImageGenerationItem {
|
||||
id: "image-call".to_string(),
|
||||
status: "completed".to_string(),
|
||||
revised_prompt: Some("paint a blue whale".to_string()),
|
||||
image: GeneratedImageRef::Inline {
|
||||
result: "cG5n".to_string(),
|
||||
},
|
||||
transparent_background: None,
|
||||
failure: None,
|
||||
saved_path: None,
|
||||
input_images: vec![ImageGenerationInputImage {
|
||||
path: AbsolutePathBuf::from_absolute_path(home.path().join("reference.png"))
|
||||
.expect("absolute input path"),
|
||||
sha256: "ab".repeat(32),
|
||||
}],
|
||||
imagegen_request_id: None,
|
||||
generation_id: None,
|
||||
};
|
||||
let image_completion =
|
||||
RolloutItem::EventMsg(EventMsg::ImageGenerationEnd(ImageGenerationEndEvent {
|
||||
call_id: expected_item.id.clone(),
|
||||
status: expected_item.status.clone(),
|
||||
revised_prompt: expected_item.revised_prompt.clone(),
|
||||
image: expected_item.image.clone(),
|
||||
transparent_background: expected_item.transparent_background,
|
||||
failure: expected_item.failure.clone(),
|
||||
saved_path: expected_item.saved_path.clone(),
|
||||
input_images: expected_item.input_images.clone(),
|
||||
}));
|
||||
let path = write_rollout(
|
||||
home.path(),
|
||||
|
||||
Reference in New Issue
Block a user