Add item streaming events (#5546)
Adds AgentMessageContentDelta, ReasoningContentDelta, ReasoningRawContentDelta item streaming events while maintaining compatibility for old events. --------- Co-authored-by: Owen Lin <owen@openai.com>
This commit is contained in:
@@ -546,6 +546,10 @@ pub enum EventMsg {
|
||||
|
||||
ItemStarted(ItemStartedEvent),
|
||||
ItemCompleted(ItemCompletedEvent),
|
||||
|
||||
AgentMessageContentDelta(AgentMessageContentDeltaEvent),
|
||||
ReasoningContentDelta(ReasoningContentDeltaEvent),
|
||||
ReasoningRawContentDelta(ReasoningRawContentDeltaEvent),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, TS, JsonSchema)]
|
||||
@@ -560,6 +564,17 @@ pub struct ItemStartedEvent {
|
||||
pub item: TurnItem,
|
||||
}
|
||||
|
||||
impl HasLegacyEvent for ItemStartedEvent {
|
||||
fn as_legacy_events(&self, _: bool) -> Vec<EventMsg> {
|
||||
match &self.item {
|
||||
TurnItem::WebSearch(item) => vec![EventMsg::WebSearchBegin(WebSearchBeginEvent {
|
||||
call_id: item.id.clone(),
|
||||
})],
|
||||
_ => Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, TS, JsonSchema)]
|
||||
pub struct ItemCompletedEvent {
|
||||
pub thread_id: ConversationId,
|
||||
@@ -567,6 +582,84 @@ pub struct ItemCompletedEvent {
|
||||
pub item: TurnItem,
|
||||
}
|
||||
|
||||
pub trait HasLegacyEvent {
|
||||
fn as_legacy_events(&self, show_raw_agent_reasoning: bool) -> Vec<EventMsg>;
|
||||
}
|
||||
|
||||
impl HasLegacyEvent for ItemCompletedEvent {
|
||||
fn as_legacy_events(&self, show_raw_agent_reasoning: bool) -> Vec<EventMsg> {
|
||||
self.item.as_legacy_events(show_raw_agent_reasoning)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, TS, JsonSchema)]
|
||||
pub struct AgentMessageContentDeltaEvent {
|
||||
pub thread_id: String,
|
||||
pub turn_id: String,
|
||||
pub item_id: String,
|
||||
pub delta: String,
|
||||
}
|
||||
|
||||
impl HasLegacyEvent for AgentMessageContentDeltaEvent {
|
||||
fn as_legacy_events(&self, _: bool) -> Vec<EventMsg> {
|
||||
vec![EventMsg::AgentMessageDelta(AgentMessageDeltaEvent {
|
||||
delta: self.delta.clone(),
|
||||
})]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, TS, JsonSchema)]
|
||||
pub struct ReasoningContentDeltaEvent {
|
||||
pub thread_id: String,
|
||||
pub turn_id: String,
|
||||
pub item_id: String,
|
||||
pub delta: String,
|
||||
}
|
||||
|
||||
impl HasLegacyEvent for ReasoningContentDeltaEvent {
|
||||
fn as_legacy_events(&self, _: bool) -> Vec<EventMsg> {
|
||||
vec![EventMsg::AgentReasoningDelta(AgentReasoningDeltaEvent {
|
||||
delta: self.delta.clone(),
|
||||
})]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, TS, JsonSchema)]
|
||||
pub struct ReasoningRawContentDeltaEvent {
|
||||
pub thread_id: String,
|
||||
pub turn_id: String,
|
||||
pub item_id: String,
|
||||
pub delta: String,
|
||||
}
|
||||
|
||||
impl HasLegacyEvent for ReasoningRawContentDeltaEvent {
|
||||
fn as_legacy_events(&self, _: bool) -> Vec<EventMsg> {
|
||||
vec![EventMsg::AgentReasoningRawContentDelta(
|
||||
AgentReasoningRawContentDeltaEvent {
|
||||
delta: self.delta.clone(),
|
||||
},
|
||||
)]
|
||||
}
|
||||
}
|
||||
|
||||
impl HasLegacyEvent for EventMsg {
|
||||
fn as_legacy_events(&self, show_raw_agent_reasoning: bool) -> Vec<EventMsg> {
|
||||
match self {
|
||||
EventMsg::ItemCompleted(event) => event.as_legacy_events(show_raw_agent_reasoning),
|
||||
EventMsg::AgentMessageContentDelta(event) => {
|
||||
event.as_legacy_events(show_raw_agent_reasoning)
|
||||
}
|
||||
EventMsg::ReasoningContentDelta(event) => {
|
||||
event.as_legacy_events(show_raw_agent_reasoning)
|
||||
}
|
||||
EventMsg::ReasoningRawContentDelta(event) => {
|
||||
event.as_legacy_events(show_raw_agent_reasoning)
|
||||
}
|
||||
_ => Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema, TS)]
|
||||
#[ts(optional_fields = nullable)]
|
||||
pub struct ExitedReviewModeEvent {
|
||||
@@ -1392,10 +1485,42 @@ pub enum TurnAbortReason {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::items::UserMessageItem;
|
||||
use crate::items::WebSearchItem;
|
||||
use anyhow::Result;
|
||||
use serde_json::json;
|
||||
use tempfile::NamedTempFile;
|
||||
|
||||
#[test]
|
||||
fn item_started_event_from_web_search_emits_begin_event() {
|
||||
let event = ItemStartedEvent {
|
||||
thread_id: ConversationId::new(),
|
||||
turn_id: "turn-1".into(),
|
||||
item: TurnItem::WebSearch(WebSearchItem {
|
||||
id: "search-1".into(),
|
||||
query: "find docs".into(),
|
||||
}),
|
||||
};
|
||||
|
||||
let legacy_events = event.as_legacy_events(false);
|
||||
assert_eq!(legacy_events.len(), 1);
|
||||
match &legacy_events[0] {
|
||||
EventMsg::WebSearchBegin(event) => assert_eq!(event.call_id, "search-1"),
|
||||
_ => panic!("expected WebSearchBegin event"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn item_started_event_from_non_web_search_emits_no_legacy_events() {
|
||||
let event = ItemStartedEvent {
|
||||
thread_id: ConversationId::new(),
|
||||
turn_id: "turn-1".into(),
|
||||
item: TurnItem::UserMessage(UserMessageItem::new(&[])),
|
||||
};
|
||||
|
||||
assert!(event.as_legacy_events(false).is_empty());
|
||||
}
|
||||
|
||||
/// Serialize Event to verify that its JSON representation has the expected
|
||||
/// amount of nesting.
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user