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:
Krish Chainani
2026-10-15 19:51:22 +00:00
committed by copyberry
parent 1ef30bcbe7
commit aabf13307a
64 changed files with 1229 additions and 125 deletions
+39
View File
@@ -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?;