mirror of
https://github.com/openai/codex.git
synced 2026-10-20 17:38:45 +00:00
Store generated image items as attachment references (#54398)
## What changed - Upload generated images to the attachment store and expose `fileId` in image-generation items when available, with inline `result` fallback on upload or decoding failure. - Preserve file references through legacy events, persisted rollouts, thread history, and task output summaries. Keep model-facing image preparation separate. - Add attachment request tags and mark generated-image uploads with `usage=image_gen`, preserving the thread's ephemeral setting. - Update public schemas and SDK types to accept either image representation, retain the Python `ImageGenerationThreadItem` alias, and prefer `fileId` when reading records containing both fields. ## Testing Add coverage for file-backed generation and rollout persistence, legacy history replay, inline and transitional serialization, task output visibility, and Python SDK alias compatibility. GitOrigin-RevId: f7a2bb5707e2f277348a547a988bc33c8e96d891
This commit is contained in:
committed by
copyberry
parent
1ef30bcbe7
commit
aabf13307a
@@ -1,5 +1,6 @@
|
||||
use super::*;
|
||||
use crate::test_support::boxed_future;
|
||||
use codex_app_server_protocol::GeneratedImageRef;
|
||||
use codex_app_server_protocol::ImageGenerationItem;
|
||||
use codex_app_server_protocol::PluginAvailability;
|
||||
use codex_utils_absolute_path::test_support::PathExt;
|
||||
@@ -856,7 +857,9 @@ pub(super) fn handle_image_generation_end(
|
||||
id: call_id.into(),
|
||||
status: status.into(),
|
||||
revised_prompt,
|
||||
result: String::new(),
|
||||
image: GeneratedImageRef::Inline {
|
||||
result: String::new(),
|
||||
},
|
||||
transparent_background: None,
|
||||
failure: None,
|
||||
saved_path,
|
||||
|
||||
@@ -13,6 +13,7 @@ use codex_app_server_protocol::DynamicToolFunctionSpec;
|
||||
use codex_app_server_protocol::DynamicToolNamespaceSpec;
|
||||
use codex_app_server_protocol::DynamicToolNamespaceTool;
|
||||
use codex_app_server_protocol::DynamicToolSpec;
|
||||
use codex_app_server_protocol::GeneratedImageRef;
|
||||
use codex_app_server_protocol::GetAccountParams;
|
||||
use codex_app_server_protocol::GetAccountResponse;
|
||||
use codex_app_server_protocol::ImageReference;
|
||||
@@ -1550,7 +1551,14 @@ fn turn_summary(turn: &Turn, include_outputs: bool, output_chars: usize) -> Valu
|
||||
"savedPath": item.saved_path
|
||||
});
|
||||
if include_outputs {
|
||||
image["result"] = output_summary(&item.result, output_chars);
|
||||
match &item.image {
|
||||
GeneratedImageRef::Inline { result } => {
|
||||
image["result"] = output_summary(result, output_chars);
|
||||
}
|
||||
GeneratedImageRef::File { file_id } => {
|
||||
image["fileId"] = Value::String(file_id.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
image
|
||||
}
|
||||
|
||||
@@ -400,6 +400,45 @@ fn activity_metadata_is_retained_without_including_outputs() -> color_eyre::Resu
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// File-backed generated images retain their reference when task output is requested without
|
||||
/// exposing that output when the caller asks for metadata only.
|
||||
#[test]
|
||||
fn file_backed_image_generation_is_retained_as_a_reference() -> color_eyre::Result<()> {
|
||||
let turn: Turn = serde_json::from_value(json!({
|
||||
"id": "turn-1",
|
||||
"status": "completed",
|
||||
"items": [{
|
||||
"type": "imageGeneration",
|
||||
"id": "image-1",
|
||||
"status": "completed",
|
||||
"fileId": "file-generated-image"
|
||||
}]
|
||||
}))?;
|
||||
|
||||
assert_eq!(
|
||||
turn_summary(&turn, /*include_outputs*/ true, DEFAULT_OUTPUT_CHARS)["items"][0],
|
||||
json!({
|
||||
"type": "imageGeneration",
|
||||
"id": "image-1",
|
||||
"status": "completed",
|
||||
"revisedPrompt": null,
|
||||
"savedPath": null,
|
||||
"fileId": "file-generated-image"
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
turn_summary(&turn, /*include_outputs*/ false, DEFAULT_OUTPUT_CHARS)["items"][0],
|
||||
json!({
|
||||
"type": "imageGeneration",
|
||||
"id": "image-1",
|
||||
"status": "completed",
|
||||
"revisedPrompt": null,
|
||||
"savedPath": null
|
||||
})
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn task_management_tools_use_existing_app_server_operations() -> color_eyre::Result<()> {
|
||||
let (codex_home, server, source, target) = test_server(json!({})).await?;
|
||||
|
||||
@@ -7,6 +7,7 @@ use crate::history_cell::HistoryCell;
|
||||
use crate::test_support::PathBufExt;
|
||||
use crate::test_support::test_path_buf;
|
||||
use codex_app_server_protocol::FileUpdateChange;
|
||||
use codex_app_server_protocol::GeneratedImageRef;
|
||||
use codex_app_server_protocol::ImageGenerationItem;
|
||||
use codex_app_server_protocol::PatchApplyStatus;
|
||||
use codex_app_server_protocol::PatchChangeKind;
|
||||
@@ -124,7 +125,9 @@ fn tool_and_notice_projection_uses_normal_transcript_presentation() {
|
||||
id: "image-1".to_string(),
|
||||
status: "completed".to_string(),
|
||||
revised_prompt: Some("A diagram of the history pages".to_string()),
|
||||
result: String::new(),
|
||||
image: GeneratedImageRef::Inline {
|
||||
result: String::new(),
|
||||
},
|
||||
transparent_background: None,
|
||||
failure: None,
|
||||
saved_path: None,
|
||||
|
||||
Reference in New Issue
Block a user