use crate::protocol::AgentMessageEvent; use crate::protocol::AgentReasoningEvent; use crate::protocol::AgentReasoningRawContentEvent; use crate::protocol::EventMsg; use crate::protocol::UserMessageEvent; use crate::protocol::WebSearchEndEvent; use crate::user_input::UserInput; use schemars::JsonSchema; use serde::Deserialize; use serde::Serialize; use ts_rs::TS; #[derive(Debug, Clone, Deserialize, Serialize, TS, JsonSchema)] pub enum TurnItem { UserMessage(UserMessageItem), AgentMessage(AgentMessageItem), Reasoning(ReasoningItem), WebSearch(WebSearchItem), } #[derive(Debug, Clone, Deserialize, Serialize, TS, JsonSchema)] pub struct UserMessageItem { pub id: String, pub content: Vec, } #[derive(Debug, Clone, Deserialize, Serialize, TS, JsonSchema)] pub enum AgentMessageContent { Text { text: String }, } #[derive(Debug, Clone, Deserialize, Serialize, TS, JsonSchema)] pub struct AgentMessageItem { pub id: String, pub content: Vec, } #[derive(Debug, Clone, Deserialize, Serialize, TS, JsonSchema)] pub struct ReasoningItem { pub id: String, pub summary_text: Vec, #[serde(default)] pub raw_content: Vec, } #[derive(Debug, Clone, Deserialize, Serialize, TS, JsonSchema)] pub struct WebSearchItem { pub id: String, pub query: String, } impl UserMessageItem { pub fn new(content: &[UserInput]) -> Self { Self { id: uuid::Uuid::new_v4().to_string(), content: content.to_vec(), } } pub fn as_legacy_event(&self) -> EventMsg { EventMsg::UserMessage(UserMessageEvent { message: self.message(), images: Some(self.image_urls()), }) } pub fn message(&self) -> String { self.content .iter() .map(|c| match c { UserInput::Text { text } => text.clone(), _ => String::new(), }) .collect::>() .join("") } pub fn image_urls(&self) -> Vec { self.content .iter() .filter_map(|c| match c { UserInput::Image { image_url } => Some(image_url.clone()), _ => None, }) .collect() } } impl AgentMessageItem { pub fn new(content: &[AgentMessageContent]) -> Self { Self { id: uuid::Uuid::new_v4().to_string(), content: content.to_vec(), } } pub fn as_legacy_events(&self) -> Vec { self.content .iter() .map(|c| match c { AgentMessageContent::Text { text } => EventMsg::AgentMessage(AgentMessageEvent { message: text.clone(), }), }) .collect() } } impl ReasoningItem { pub fn as_legacy_events(&self, show_raw_agent_reasoning: bool) -> Vec { let mut events = Vec::new(); for summary in &self.summary_text { events.push(EventMsg::AgentReasoning(AgentReasoningEvent { text: summary.clone(), })); } if show_raw_agent_reasoning { for entry in &self.raw_content { events.push(EventMsg::AgentReasoningRawContent( AgentReasoningRawContentEvent { text: entry.clone(), }, )); } } events } } impl WebSearchItem { pub fn as_legacy_event(&self) -> EventMsg { EventMsg::WebSearchEnd(WebSearchEndEvent { call_id: self.id.clone(), query: self.query.clone(), }) } } impl TurnItem { pub fn id(&self) -> String { match self { TurnItem::UserMessage(item) => item.id.clone(), TurnItem::AgentMessage(item) => item.id.clone(), TurnItem::Reasoning(item) => item.id.clone(), TurnItem::WebSearch(item) => item.id.clone(), } } pub fn as_legacy_events(&self, show_raw_agent_reasoning: bool) -> Vec { match self { TurnItem::UserMessage(item) => vec![item.as_legacy_event()], TurnItem::AgentMessage(item) => item.as_legacy_events(), TurnItem::WebSearch(item) => vec![item.as_legacy_event()], TurnItem::Reasoning(item) => item.as_legacy_events(show_raw_agent_reasoning), } } }