rs_adk/events/
structured.rs1use serde_json::Value;
7
8#[derive(Debug, Clone, PartialEq, Eq)]
10pub enum EventType {
11 Thought,
13 Content,
15 ToolCall,
17 ToolResult,
19 CallCode,
21 CodeResult,
23 Error,
25 Activity,
27 ToolConfirmation,
29 Finished,
31}
32
33#[derive(Debug, Clone)]
35pub enum StructuredEvent {
36 Thought {
38 text: String,
40 },
41 Content {
43 text: String,
45 author: String,
47 },
48 ToolCall {
50 name: String,
52 args: Value,
54 call_id: Option<String>,
56 },
57 ToolResult {
59 name: String,
61 result: Value,
63 },
64 Error {
66 message: String,
68 },
69 Activity {
71 description: String,
73 },
74 ToolConfirmation {
76 hint: Option<String>,
78 confirmed: bool,
80 },
81 Finished,
83}
84
85impl StructuredEvent {
86 pub fn event_type(&self) -> EventType {
88 match self {
89 Self::Thought { .. } => EventType::Thought,
90 Self::Content { .. } => EventType::Content,
91 Self::ToolCall { .. } => EventType::ToolCall,
92 Self::ToolResult { .. } => EventType::ToolResult,
93 Self::Error { .. } => EventType::Error,
94 Self::Activity { .. } => EventType::Activity,
95 Self::ToolConfirmation { .. } => EventType::ToolConfirmation,
96 Self::Finished => EventType::Finished,
97 }
98 }
99}
100
101pub fn to_structured_events(event: &super::Event) -> Vec<StructuredEvent> {
105 let mut out = Vec::new();
106
107 if let Some(text) = &event.content {
109 if !text.is_empty() {
110 out.push(StructuredEvent::Content {
111 text: text.clone(),
112 author: event.author.clone(),
113 });
114 }
115 }
116
117 if let Some(target) = &event.actions.transfer_to_agent {
119 out.push(StructuredEvent::Activity {
120 description: format!("Transferring to agent: {target}"),
121 });
122 }
123
124 if event.actions.escalate {
126 out.push(StructuredEvent::Activity {
127 description: "Escalating to human/parent agent".to_string(),
128 });
129 }
130
131 for (key, value) in &event.actions.state_delta {
133 if key.starts_with("tool:result:") {
134 let tool_name = key.strip_prefix("tool:result:").unwrap_or(key);
135 out.push(StructuredEvent::ToolResult {
136 name: tool_name.to_string(),
137 result: value.clone(),
138 });
139 }
140 }
141
142 out
143}
144
145#[cfg(test)]
146mod tests {
147 use super::*;
148 use crate::events::{Event, EventActions};
149 use std::collections::HashMap;
150
151 #[test]
152 fn event_type_classification() {
153 let thought = StructuredEvent::Thought { text: "hmm".into() };
154 assert_eq!(thought.event_type(), EventType::Thought);
155
156 let content = StructuredEvent::Content {
157 text: "hi".into(),
158 author: "model".into(),
159 };
160 assert_eq!(content.event_type(), EventType::Content);
161
162 assert_eq!(StructuredEvent::Finished.event_type(), EventType::Finished);
163 }
164
165 #[test]
166 fn convert_event_with_content() {
167 let event = Event::new("model", Some("Hello there!".to_string()));
168 let structured = to_structured_events(&event);
169 assert_eq!(structured.len(), 1);
170 match &structured[0] {
171 StructuredEvent::Content { text, author } => {
172 assert_eq!(text, "Hello there!");
173 assert_eq!(author, "model");
174 }
175 other => panic!("expected Content, got: {other:?}"),
176 }
177 }
178
179 #[test]
180 fn convert_event_with_transfer() {
181 let actions = EventActions::transfer("helper");
182 let event = Event::new("agent", None).with_actions(actions);
183 let structured = to_structured_events(&event);
184 assert_eq!(structured.len(), 1);
185 match &structured[0] {
186 StructuredEvent::Activity { description } => {
187 assert!(description.contains("helper"));
188 }
189 other => panic!("expected Activity, got: {other:?}"),
190 }
191 }
192
193 #[test]
194 fn convert_event_with_content_and_transfer() {
195 let actions = EventActions::transfer("target");
196 let event = Event::new("agent", Some("Handing off".to_string())).with_actions(actions);
197 let structured = to_structured_events(&event);
198 assert_eq!(structured.len(), 2);
199 assert_eq!(structured[0].event_type(), EventType::Content);
200 assert_eq!(structured[1].event_type(), EventType::Activity);
201 }
202
203 #[test]
204 fn convert_empty_event() {
205 let event = Event::new("system", None);
206 let structured = to_structured_events(&event);
207 assert!(structured.is_empty());
208 }
209
210 #[test]
211 fn convert_event_with_tool_result_in_state_delta() {
212 let mut delta = HashMap::new();
213 delta.insert(
214 "tool:result:get_weather".to_string(),
215 serde_json::json!({"temp": 22}),
216 );
217 let actions = EventActions::state_delta(delta);
218 let event = Event::new("model", None).with_actions(actions);
219 let structured = to_structured_events(&event);
220 assert_eq!(structured.len(), 1);
221 match &structured[0] {
222 StructuredEvent::ToolResult { name, result } => {
223 assert_eq!(name, "get_weather");
224 assert_eq!(result["temp"], 22);
225 }
226 other => panic!("expected ToolResult, got: {other:?}"),
227 }
228 }
229
230 #[test]
231 fn convert_event_with_escalation() {
232 let actions = EventActions::escalate();
233 let event = Event::new("agent", None).with_actions(actions);
234 let structured = to_structured_events(&event);
235 assert_eq!(structured.len(), 1);
236 assert_eq!(structured[0].event_type(), EventType::Activity);
237 }
238}