1use super::envelope_meta::{
11 clear_serialize_run_hint_if_matches, serialize_run_hint, set_serialize_run_hint,
12 take_runtime_event_envelope_meta,
13};
14use super::wire::{from_data_value, to_data_value, EventEnvelope};
15use crate::runtime::inference::TokenUsage;
16use crate::runtime::tool_call::ToolResult;
17use crate::runtime::TerminationReason;
18use crate::runtime::ToolCallOutcome;
19use serde::{Deserialize, Serialize};
20use serde_json::Value;
21use tirea_state::TrackedPatch;
22
23const fn default_tool_call_outcome() -> ToolCallOutcome {
24 ToolCallOutcome::Succeeded
25}
26
27macro_rules! run_hint_action {
29 (set, $hint:expr) => {
30 set_serialize_run_hint($hint.0, $hint.1);
31 };
32 (clear, $hint:expr) => {
33 clear_serialize_run_hint_if_matches(&$hint.0, &$hint.1);
34 };
35}
36
37macro_rules! define_agent_events {
51 (
52 data_imports { $($import:item)* }
53
54 envelope {
55 $(
56 $(#[$env_vattr:meta])*
57 $EnvVariant:ident {
58 $($(#[$env_dattr:meta])* $env_dfield:ident : $env_dfty:ty),*
59 $(,)?
60 } => $hint_action:ident
61 ),* $(,)?
62 }
63
64 standard {
65 $(
66 $(#[$std_vattr:meta])*
67 $StdVariant:ident {
68 $($(#[$std_fattr:meta])* $std_field:ident : $std_fty:ty),*
69 $(,)?
70 }
71 ),* $(,)?
72 }
73
74 wire_only {
75 $(
76 $(#[$wire_vattr:meta])*
77 $WireVariant:ident {
78 $($(#[$wire_fattr:meta])* $wire_field:ident : $wire_fty:ty),*
79 $(,)?
80 } wire_extra {
81 $($(#[$wire_eattr:meta])* $wire_efield:ident : $wire_efty:ty = $wire_expr:expr),*
82 $(,)?
83 }
84 ),* $(,)?
85 }
86
87 no_data {
88 $(
89 $(#[$nd_vattr:meta])*
90 $NoDataVariant:ident
91 ),* $(,)?
92 }
93 ) => {
94 #[derive(Debug, Clone)]
98 pub enum AgentEvent {
99 $(
100 $(#[$env_vattr])*
101 $EnvVariant {
102 thread_id: String,
103 run_id: String,
104 $($env_dfield: $env_dfty),*
105 },
106 )*
107 $(
108 $(#[$std_vattr])*
109 $StdVariant { $($std_field: $std_fty),* },
110 )*
111 $(
112 $(#[$wire_vattr])*
113 $WireVariant { $($wire_field: $wire_fty),* },
114 )*
115 $(
116 $(#[$nd_vattr])*
117 $NoDataVariant,
118 )*
119 }
120
121 #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
124 #[serde(rename_all = "snake_case")]
125 pub(crate) enum AgentEventType {
126 $($EnvVariant,)*
127 $($StdVariant,)*
128 $($WireVariant,)*
129 $($NoDataVariant,)*
130 }
131
132 impl AgentEvent {
135 pub(crate) fn event_type(&self) -> AgentEventType {
136 match self {
137 $(Self::$EnvVariant { .. } => AgentEventType::$EnvVariant,)*
138 $(Self::$StdVariant { .. } => AgentEventType::$StdVariant,)*
139 $(Self::$WireVariant { .. } => AgentEventType::$WireVariant,)*
140 $(Self::$NoDataVariant => AgentEventType::$NoDataVariant,)*
141 }
142 }
143 }
144
145 mod data {
148 $($import)*
149
150 $(
151 #[derive(Debug, Clone, Serialize, Deserialize)]
152 pub(super) struct $EnvVariant {
153 $($(#[$env_dattr])* pub $env_dfield: $env_dfty,)*
154 }
155 )*
156
157 $(
158 #[derive(Debug, Clone, Serialize, Deserialize)]
159 pub(super) struct $StdVariant {
160 $($(#[$std_fattr])* pub $std_field: $std_fty,)*
161 }
162 )*
163
164 $(
165 #[derive(Debug, Clone, Serialize, Deserialize)]
166 pub(super) struct $WireVariant {
167 $($(#[$wire_fattr])* pub $wire_field: $wire_fty,)*
168 $($(#[$wire_eattr])* pub $wire_efield: $wire_efty,)*
169 }
170 )*
171 }
172
173 impl Serialize for AgentEvent {
176 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
177 where
178 S: serde::Serializer,
179 {
180 let event_type = self.event_type();
181
182 let run_hint = match self {
183 $(
184 Self::$EnvVariant { run_id, thread_id, .. } => {
185 Some((run_id.clone(), thread_id.clone()))
186 }
187 )*
188 _ => serialize_run_hint(),
189 };
190
191 let runtime_meta = take_runtime_event_envelope_meta(
192 event_type,
193 run_hint
194 .as_ref()
195 .map(|(r, t)| (r.as_str(), t.as_str())),
196 );
197 let meta_run_id = || runtime_meta.as_ref().map(|m| m.run_id.clone());
198 let meta_thread_id = || runtime_meta.as_ref().map(|m| m.thread_id.clone());
199 let meta_seq = || runtime_meta.as_ref().map(|m| m.seq);
200 let meta_timestamp_ms = || runtime_meta.as_ref().map(|m| m.timestamp_ms);
201 let meta_step_id = || runtime_meta.as_ref().and_then(|m| m.step_id.clone());
202
203 let envelope = match self {
204 $(
205 Self::$EnvVariant { thread_id, run_id, $($env_dfield),* } => EventEnvelope {
206 event_type: AgentEventType::$EnvVariant,
207 run_id: meta_run_id().or_else(|| Some(run_id.clone())),
208 thread_id: meta_thread_id().or_else(|| Some(thread_id.clone())),
209 seq: meta_seq(),
210 timestamp_ms: meta_timestamp_ms(),
211 step_id: meta_step_id(),
212 data: to_data_value(&data::$EnvVariant {
213 $($env_dfield: $env_dfield.clone(),)*
214 }).map_err(serde::ser::Error::custom)?,
215 },
216 )*
217 $(
218 Self::$StdVariant { $($std_field),* } => EventEnvelope {
219 event_type: AgentEventType::$StdVariant,
220 run_id: meta_run_id(),
221 thread_id: meta_thread_id(),
222 seq: meta_seq(),
223 timestamp_ms: meta_timestamp_ms(),
224 step_id: meta_step_id(),
225 data: to_data_value(&data::$StdVariant {
226 $($std_field: $std_field.clone(),)*
227 }).map_err(serde::ser::Error::custom)?,
228 },
229 )*
230 $(
231 Self::$WireVariant { $($wire_field),* } => EventEnvelope {
232 event_type: AgentEventType::$WireVariant,
233 run_id: meta_run_id(),
234 thread_id: meta_thread_id(),
235 seq: meta_seq(),
236 timestamp_ms: meta_timestamp_ms(),
237 step_id: meta_step_id(),
238 data: to_data_value(&data::$WireVariant {
239 $($wire_field: $wire_field.clone(),)*
240 $($wire_efield: $wire_expr,)*
241 }).map_err(serde::ser::Error::custom)?,
242 },
243 )*
244 $(
245 Self::$NoDataVariant => EventEnvelope {
246 event_type: AgentEventType::$NoDataVariant,
247 run_id: meta_run_id(),
248 thread_id: meta_thread_id(),
249 seq: meta_seq(),
250 timestamp_ms: meta_timestamp_ms(),
251 step_id: meta_step_id(),
252 data: None,
253 },
254 )*
255 };
256
257 match self {
259 $(
260 Self::$EnvVariant { run_id, thread_id, .. } => {
261 let hint = runtime_meta
262 .as_ref()
263 .map(|meta| (meta.run_id.clone(), meta.thread_id.clone()))
264 .unwrap_or_else(|| (run_id.clone(), thread_id.clone()));
265 run_hint_action!($hint_action, hint);
266 }
267 )*
268 _ => {}
269 }
270
271 envelope.serialize(serializer)
272 }
273 }
274
275 impl<'de> Deserialize<'de> for AgentEvent {
278 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
279 where
280 D: serde::Deserializer<'de>,
281 {
282 let envelope = EventEnvelope::deserialize(deserializer)?;
283 match envelope.event_type {
284 $(
285 AgentEventType::$EnvVariant => {
286 let d: data::$EnvVariant = from_data_value(envelope.data)?;
287 let run_id = envelope.run_id.ok_or_else(|| {
288 serde::de::Error::custom(concat!(
289 stringify!($EnvVariant), " missing run_id"
290 ))
291 })?;
292 let thread_id = envelope.thread_id.ok_or_else(|| {
293 serde::de::Error::custom(concat!(
294 stringify!($EnvVariant), " missing thread_id"
295 ))
296 })?;
297 Ok(Self::$EnvVariant {
298 thread_id,
299 run_id,
300 $($env_dfield: d.$env_dfield),*
301 })
302 }
303 )*
304 $(
305 AgentEventType::$StdVariant => {
306 let d: data::$StdVariant = from_data_value(envelope.data)?;
307 Ok(Self::$StdVariant { $($std_field: d.$std_field),* })
308 }
309 )*
310 $(
311 AgentEventType::$WireVariant => {
312 let d: data::$WireVariant = from_data_value(envelope.data)?;
313 $(let _ = d.$wire_efield;)*
314 Ok(Self::$WireVariant { $($wire_field: d.$wire_field),* })
315 }
316 )*
317 $(
318 AgentEventType::$NoDataVariant => Ok(Self::$NoDataVariant),
319 )*
320 }
321 }
322 }
323 };
324}
325
326define_agent_events! {
335 data_imports {
336 use crate::runtime::TerminationReason;
337 use crate::runtime::ToolCallOutcome;
338 use crate::runtime::inference::TokenUsage;
339 use crate::runtime::tool_call::ToolResult;
340 use serde::{Deserialize, Serialize};
341 use serde_json::Value;
342 use tirea_state::TrackedPatch;
343 }
344
345 envelope {
346 RunStart {
348 #[serde(skip_serializing_if = "Option::is_none")]
349 parent_run_id: Option<String>,
350 } => set,
351
352 RunFinish {
354 #[serde(skip_serializing_if = "Option::is_none")]
355 result: Option<Value>,
356 termination: TerminationReason,
358 } => clear,
359 }
360
361 standard {
362 TextDelta { delta: String },
364
365 ReasoningDelta { delta: String },
367
368 ReasoningEncryptedValue { encrypted_value: String },
370
371 ToolCallStart { id: String, name: String },
373
374 ToolCallDelta { id: String, args_delta: String },
376
377 ToolCallReady { id: String, name: String, arguments: Value },
379
380 StepStart {
382 #[serde(default)]
384 message_id: String,
385 },
386
387 InferenceComplete {
389 model: String,
391 #[serde(skip_serializing_if = "Option::is_none")]
393 usage: Option<TokenUsage>,
394 duration_ms: u64,
396 },
397
398 StateSnapshot { snapshot: Value },
400
401 StateDelta { delta: Vec<Value> },
403
404 MessagesSnapshot { messages: Vec<Value> },
406
407 ActivitySnapshot {
409 message_id: String,
410 activity_type: String,
411 content: Value,
412 #[serde(skip_serializing_if = "Option::is_none")]
413 replace: Option<bool>,
414 },
415
416 ActivityDelta {
418 message_id: String,
419 activity_type: String,
420 patch: Vec<Value>,
421 },
422
423 ToolCallResumed { target_id: String, result: Value },
425
426 Error {
428 message: String,
429 #[serde(skip_serializing_if = "Option::is_none")]
430 code: Option<String>,
431 },
432 }
433
434 wire_only {
435 ToolCallDone {
437 id: String,
438 result: ToolResult,
439 #[serde(skip_serializing_if = "Option::is_none")]
440 patch: Option<TrackedPatch>,
441 #[serde(default)]
443 message_id: String,
444 } wire_extra {
445 #[serde(default = "super::default_tool_call_outcome")]
446 outcome: ToolCallOutcome = ToolCallOutcome::from_tool_result(result),
447 }
448 }
449
450 no_data {
451 StepEnd,
453 }
454}
455
456impl AgentEvent {
461 pub fn extract_response(result: &Option<Value>) -> String {
463 result
464 .as_ref()
465 .and_then(|v| v.get("response"))
466 .and_then(|r| r.as_str())
467 .unwrap_or_default()
468 .to_string()
469 }
470}