image-gen-event/client_processing (#13512)

enabling client-side to process with image-generation capabilities
(setting app-server)
This commit is contained in:
Won Park
2026-03-04 16:54:38 -08:00
committed by GitHub
parent 84ba9f8e74
commit 229e6d0347
41 changed files with 1244 additions and 6 deletions

View File

@@ -1086,6 +1086,10 @@ pub enum EventMsg {
WebSearchEnd(WebSearchEndEvent),
ImageGenerationBegin(ImageGenerationBeginEvent),
ImageGenerationEnd(ImageGenerationEndEvent),
/// Notification that the server is about to execute a command.
ExecCommandBegin(ExecCommandBeginEvent),
@@ -1367,6 +1371,11 @@ impl HasLegacyEvent for ItemStartedEvent {
TurnItem::WebSearch(item) => vec![EventMsg::WebSearchBegin(WebSearchBeginEvent {
call_id: item.id.clone(),
})],
TurnItem::ImageGeneration(item) => {
vec![EventMsg::ImageGenerationBegin(ImageGenerationBeginEvent {
call_id: item.id.clone(),
})]
}
_ => Vec::new(),
}
}
@@ -1876,6 +1885,21 @@ pub struct WebSearchEndEvent {
pub action: WebSearchAction,
}
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema, TS)]
pub struct ImageGenerationBeginEvent {
pub call_id: String,
}
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema, TS)]
pub struct ImageGenerationEndEvent {
pub call_id: String,
pub status: String,
#[serde(skip_serializing_if = "Option::is_none")]
#[ts(optional)]
pub revised_prompt: Option<String>,
pub result: String,
}
// Conversation kept for backward compatibility.
/// Response payload for `Op::GetHistory` containing the current session's
/// in-memory transcript.
@@ -3115,6 +3139,7 @@ pub struct CollabResumeEndEvent {
#[cfg(test)]
mod tests {
use super::*;
use crate::items::ImageGenerationItem;
use crate::items::UserMessageItem;
use crate::items::WebSearchItem;
use anyhow::Result;
@@ -3235,6 +3260,53 @@ mod tests {
assert!(event.as_legacy_events(false).is_empty());
}
#[test]
fn item_started_event_from_image_generation_emits_begin_event() {
let event = ItemStartedEvent {
thread_id: ThreadId::new(),
turn_id: "turn-1".into(),
item: TurnItem::ImageGeneration(ImageGenerationItem {
id: "ig-1".into(),
status: "in_progress".into(),
revised_prompt: None,
result: String::new(),
}),
};
let legacy_events = event.as_legacy_events(false);
assert_eq!(legacy_events.len(), 1);
match &legacy_events[0] {
EventMsg::ImageGenerationBegin(event) => assert_eq!(event.call_id, "ig-1"),
_ => panic!("expected ImageGenerationBegin event"),
}
}
#[test]
fn item_completed_event_from_image_generation_emits_end_event() {
let event = ItemCompletedEvent {
thread_id: ThreadId::new(),
turn_id: "turn-1".into(),
item: TurnItem::ImageGeneration(ImageGenerationItem {
id: "ig-1".into(),
status: "completed".into(),
revised_prompt: Some("A tiny blue square".into()),
result: "Zm9v".into(),
}),
};
let legacy_events = event.as_legacy_events(false);
assert_eq!(legacy_events.len(), 1);
match &legacy_events[0] {
EventMsg::ImageGenerationEnd(event) => {
assert_eq!(event.call_id, "ig-1");
assert_eq!(event.status, "completed");
assert_eq!(event.revised_prompt.as_deref(), Some("A tiny blue square"));
assert_eq!(event.result, "Zm9v");
}
_ => panic!("expected ImageGenerationEnd event"),
}
}
#[test]
fn rollback_failed_error_does_not_affect_turn_status() {
let event = ErrorEvent {