ag_ui_core/
event.rs

1use crate::types::ids::{MessageId, RunId, ThreadId, ToolCallId};
2use crate::types::message::{Message, Role};
3use crate::{AgentState, JsonValue};
4use serde::{Deserialize, Serialize};
5
6/// Event types for AG-UI protocol
7/// Event types for AG-UI protocol
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
9#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
10pub enum EventType {
11    /// Event indicating the start of a text message
12    TextMessageStart,
13    /// Event containing a piece of text message content
14    TextMessageContent,
15    /// Event indicating the end of a text message
16    TextMessageEnd,
17    /// Event containing a chunk of text message content
18    TextMessageChunk,
19    /// Event indicating the start of a thinking text message
20    ThinkingTextMessageStart,
21    /// Event indicating a piece of a thinking text message
22    ThinkingTextMessageContent,
23    /// Event indicating the end of a thinking text message
24    ThinkingTextMessageEnd,
25    /// Event indicating the start of a tool call
26    ToolCallStart,
27    /// Event containing tool call arguments
28    ToolCallArgs,
29    /// Event indicating the end of a tool call
30    ToolCallEnd,
31    /// Event containing a chunk of tool call content
32    ToolCallChunk,
33    /// Event containing the result of a tool call
34    ToolCallResult,
35    /// Event indicating the start of a thinking step event
36    ThinkingStart,
37    /// Event indicating the end of a thinking step event
38    ThinkingEnd,
39    /// Event containing a snapshot of the state
40    StateSnapshot,
41    /// Event containing a delta of the state
42    StateDelta,
43    /// Event containing a snapshot of the messages
44    MessagesSnapshot,
45    /// Event containing a raw event
46    Raw,
47    /// Event containing a custom event
48    Custom,
49    /// Event indicating that a run has started
50    RunStarted,
51    /// Event indicating that a run has finished
52    RunFinished,
53    /// Event indicating that a run has encountered an error
54    RunError,
55    /// Event indicating that a step has started
56    StepStarted,
57    /// Event indicating that a step has finished
58    StepFinished,
59}
60
61/// Base event for all events in the Agent User Interaction Protocol.
62/// Contains common fields that are present in all event types.
63#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
64pub struct BaseEvent {
65    #[serde(skip_serializing_if = "Option::is_none")]
66    pub timestamp: Option<f64>,
67    #[serde(rename = "rawEvent", skip_serializing_if = "Option::is_none")]
68    pub raw_event: Option<JsonValue>,
69}
70
71/// Event indicating the start of a text message.
72/// This event is sent when the agent begins generating a text message.
73#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
74pub struct TextMessageStartEvent {
75    #[serde(flatten)]
76    pub base: BaseEvent,
77    #[serde(rename = "messageId")]
78    pub message_id: MessageId,
79    pub role: Role, // "assistant"
80}
81
82/// Event containing a piece of text message content.
83/// This event is sent for each chunk of content as the agent generates a message.
84#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
85pub struct TextMessageContentEvent {
86    #[serde(flatten)]
87    pub base: BaseEvent,
88    #[serde(rename = "messageId")]
89    pub message_id: MessageId,
90    pub delta: String,
91}
92
93/// Event indicating the end of a text message.
94/// This event is sent when the agent completes a text message.
95#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
96pub struct TextMessageEndEvent {
97    #[serde(flatten)]
98    pub base: BaseEvent,
99    #[serde(rename = "messageId")]
100    pub message_id: MessageId,
101}
102
103/// Event containing a chunk of text message content.
104/// This event combines start, content, and potentially end information in a single event,
105/// with optional fields that may or may not be present.
106#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
107pub struct TextMessageChunkEvent {
108    #[serde(flatten)]
109    pub base: BaseEvent,
110    #[serde(rename = "messageId", skip_serializing_if = "Option::is_none")]
111    pub message_id: Option<MessageId>,
112    pub role: Role,
113    #[serde(skip_serializing_if = "Option::is_none")]
114    pub delta: Option<String>,
115}
116
117/// Event indicating the start of a thinking text message.
118/// This event is sent when the agent begins generating internal thinking content.
119#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
120pub struct ThinkingTextMessageStartEvent {
121    #[serde(flatten)]
122    pub base: BaseEvent,
123}
124
125/// Event indicating a piece of a thinking text message.
126/// This event contains chunks of the agent's internal thinking process.
127#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
128pub struct ThinkingTextMessageContentEvent {
129    #[serde(flatten)]
130    pub base: BaseEvent,
131    pub delta: String,
132}
133
134/// Event indicating the end of a thinking text message.
135/// This event is sent when the agent completes its internal thinking process.
136#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
137pub struct ThinkingTextMessageEndEvent {
138    #[serde(flatten)]
139    pub base: BaseEvent,
140}
141
142/// Event indicating the start of a tool call.
143/// This event is sent when the agent begins to call a tool with specific parameters.
144#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
145pub struct ToolCallStartEvent {
146    #[serde(flatten)]
147    pub base: BaseEvent,
148    #[serde(rename = "toolCallId")]
149    pub tool_call_id: ToolCallId,
150    #[serde(rename = "toolCallName")]
151    pub tool_call_name: String,
152    #[serde(rename = "parentMessageId", skip_serializing_if = "Option::is_none")]
153    pub parent_message_id: Option<MessageId>,
154}
155
156/// Event containing tool call arguments.
157/// This event contains chunks of the arguments being passed to a tool.
158#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
159pub struct ToolCallArgsEvent {
160    #[serde(flatten)]
161    pub base: BaseEvent,
162    #[serde(rename = "toolCallId")]
163    pub tool_call_id: ToolCallId,
164    pub delta: String,
165}
166
167/// Event indicating the end of a tool call.
168/// This event is sent when the agent completes sending arguments to a tool.
169#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
170pub struct ToolCallEndEvent {
171    #[serde(flatten)]
172    pub base: BaseEvent,
173    #[serde(rename = "toolCallId")]
174    pub tool_call_id: ToolCallId,
175}
176
177/// Event containing the result of a tool call.
178/// This event is sent when a tool has completed execution and returns its result.
179#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
180pub struct ToolCallResultEvent {
181    #[serde(flatten)]
182    pub base: BaseEvent,
183    #[serde(rename = "messageId")]
184    pub message_id: MessageId,
185    #[serde(rename = "toolCallId")]
186    pub tool_call_id: ToolCallId,
187    pub content: String,
188    #[serde(default = "Role::tool")]
189    pub role: Role, // "tool"
190}
191
192/// Event containing a chunk of tool call content.
193/// This event combines start, args, and potentially end information in a single event,
194/// with optional fields that may or may not be present.
195#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
196pub struct ToolCallChunkEvent {
197    #[serde(flatten)]
198    pub base: BaseEvent,
199    #[serde(rename = "toolCallId", skip_serializing_if = "Option::is_none")]
200    pub tool_call_id: Option<ToolCallId>,
201    #[serde(rename = "toolCallName", skip_serializing_if = "Option::is_none")]
202    pub tool_call_name: Option<String>,
203    #[serde(rename = "parentMessageId", skip_serializing_if = "Option::is_none")]
204    pub parent_message_id: Option<MessageId>,
205    #[serde(skip_serializing_if = "Option::is_none")]
206    pub delta: Option<String>,
207}
208
209/// Event indicating the start of a thinking step event.
210/// This event is sent when the agent begins a deliberate thinking phase.
211#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
212pub struct ThinkingStartEvent {
213    #[serde(flatten)]
214    pub base: BaseEvent,
215    #[serde(skip_serializing_if = "Option::is_none")]
216    pub title: Option<String>,
217}
218
219/// Event indicating the end of a thinking step event.
220/// This event is sent when the agent completes a thinking phase.
221#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
222pub struct ThinkingEndEvent {
223    #[serde(flatten)]
224    pub base: BaseEvent,
225}
226
227/// Event containing a snapshot of the state.
228/// This event provides a complete representation of the current agent state.
229#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
230#[serde(bound(deserialize = ""))]
231pub struct StateSnapshotEvent<StateT: AgentState = JsonValue> {
232    #[serde(flatten)]
233    pub base: BaseEvent,
234    pub snapshot: StateT,
235}
236
237/// Event containing a delta of the state.
238/// This event contains JSON Patch operations (RFC 6902) that describe changes to the agent state.
239#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
240pub struct StateDeltaEvent {
241    #[serde(flatten)]
242    pub base: BaseEvent,
243    pub delta: Vec<JsonValue>,
244}
245
246/// Event containing a snapshot of the messages.
247/// This event provides a complete list of all current conversation messages.
248#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
249pub struct MessagesSnapshotEvent {
250    #[serde(flatten)]
251    pub base: BaseEvent,
252    pub messages: Vec<Message>,
253}
254
255/// Event containing a raw event.
256/// This event type allows wrapping arbitrary events from external sources.
257#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
258pub struct RawEvent {
259    #[serde(flatten)]
260    pub base: BaseEvent,
261    pub event: JsonValue,
262    #[serde(skip_serializing_if = "Option::is_none")]
263    pub source: Option<String>,
264}
265
266/// Event containing a custom event.
267/// This event type allows for application-specific custom events with arbitrary data.
268#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
269pub struct CustomEvent {
270    #[serde(flatten)]
271    pub base: BaseEvent,
272    pub name: String,
273    pub value: JsonValue,
274}
275
276/// Event indicating that a run has started.
277/// This event is sent when an agent run begins execution within a specific thread.
278#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
279pub struct RunStartedEvent {
280    #[serde(flatten)]
281    pub base: BaseEvent,
282    #[serde(rename = "threadId")]
283    pub thread_id: ThreadId,
284    #[serde(rename = "runId")]
285    pub run_id: RunId,
286}
287
288/// Event indicating that a run has finished.
289/// This event is sent when an agent run completes successfully, potentially with a result.
290#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
291pub struct RunFinishedEvent {
292    #[serde(flatten)]
293    pub base: BaseEvent,
294    #[serde(rename = "threadId")]
295    pub thread_id: ThreadId,
296    #[serde(rename = "runId")]
297    pub run_id: RunId,
298    #[serde(skip_serializing_if = "Option::is_none")]
299    pub result: Option<JsonValue>,
300}
301
302/// Event indicating that a run has encountered an error.
303/// This event is sent when an agent run fails with an error message and optional error code.
304#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
305pub struct RunErrorEvent {
306    #[serde(flatten)]
307    pub base: BaseEvent,
308    pub message: String,
309    #[serde(skip_serializing_if = "Option::is_none")]
310    pub code: Option<String>,
311}
312
313/// Event indicating that a step has started.
314/// This event is sent when a specific named step within a run begins execution.
315#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
316pub struct StepStartedEvent {
317    #[serde(flatten)]
318    pub base: BaseEvent,
319    #[serde(rename = "stepName")]
320    pub step_name: String,
321}
322
323/// Event indicating that a step has finished.
324/// This event is sent when a specific named step within a run completes execution.
325#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
326pub struct StepFinishedEvent {
327    #[serde(flatten)]
328    pub base: BaseEvent,
329    #[serde(rename = "stepName")]
330    pub step_name: String,
331}
332
333/// Union of all possible events in the Agent User Interaction Protocol.
334/// This enum represents the full set of events that can be exchanged
335/// between the agent and the client.
336#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
337#[serde(
338    tag = "type",
339    rename_all = "SCREAMING_SNAKE_CASE",
340    bound(deserialize = "")
341)]
342pub enum Event<StateT: AgentState = JsonValue> {
343    /// Signals the start of a text message from an agent.
344    /// Contains the message ID and role information.
345    TextMessageStart(TextMessageStartEvent),
346
347    /// Represents a chunk of content being added to an in-progress text message.
348    /// Contains the message ID and the text delta to append.
349    TextMessageContent(TextMessageContentEvent),
350
351    /// Signals the completion of a text message.
352    /// Contains the message ID of the completed message.
353    TextMessageEnd(TextMessageEndEvent),
354
355    /// Represents a complete or partial message chunk in a single event.
356    /// May contain optional message ID, role, and delta information.
357    TextMessageChunk(TextMessageChunkEvent),
358
359    /// Signals the start of a thinking text message.
360    /// Used for internal agent thought processes that should be displayed to the user.
361    ThinkingTextMessageStart(ThinkingTextMessageStartEvent),
362
363    /// Represents content being added to an in-progress thinking text message.
364    /// Contains the delta text to append.
365    ThinkingTextMessageContent(ThinkingTextMessageContentEvent),
366
367    /// Signals the completion of a thinking text message.
368    ThinkingTextMessageEnd(ThinkingTextMessageEndEvent),
369
370    /// Signals the start of a tool call by the agent.
371    /// Contains the tool call ID, name, and optional parent message ID.
372    ToolCallStart(ToolCallStartEvent),
373
374    /// Represents arguments being added to an in-progress tool call.
375    /// Contains the tool call ID and argument data delta.
376    ToolCallArgs(ToolCallArgsEvent),
377
378    /// Signals the completion of a tool call.
379    /// Contains the tool call ID of the completed call.
380    ToolCallEnd(ToolCallEndEvent),
381
382    /// Represents a complete or partial tool call in a single event.
383    /// May contain optional tool call ID, name, parent message ID, and delta.
384    ToolCallChunk(ToolCallChunkEvent),
385
386    /// Represents the result of a completed tool call.
387    /// Contains the message ID, tool call ID, content, and optional role.
388    ToolCallResult(ToolCallResultEvent),
389
390    /// Signals the start of a thinking process.
391    /// Contains an optional title describing the thinking process.
392    ThinkingStart(ThinkingStartEvent),
393
394    /// Signals the end of a thinking process.
395    ThinkingEnd(ThinkingEndEvent),
396
397    /// Provides a complete snapshot of the current state.
398    /// Contains the full state as a JSON value.
399    StateSnapshot(StateSnapshotEvent<StateT>),
400
401    /// Provides incremental changes to the state.
402    /// Contains a vector of delta operations to apply to the state.
403    StateDelta(StateDeltaEvent),
404
405    /// Provides a complete snapshot of all messages.
406    /// Contains a vector of all current messages.
407    MessagesSnapshot(MessagesSnapshotEvent),
408
409    /// Wraps a raw event from an external source.
410    /// Contains the original event as a JSON value and an optional source identifier.
411    Raw(RawEvent),
412
413    /// Represents a custom event type not covered by the standard events.
414    /// Contains a name identifying the custom event type and an associated value.
415    Custom(CustomEvent),
416
417    /// Signals the start of an agent run.
418    /// Contains thread ID and run ID to identify the run.
419    RunStarted(RunStartedEvent),
420
421    /// Signals the completion of an agent run.
422    /// Contains thread ID, run ID, and optional result data.
423    RunFinished(RunFinishedEvent),
424
425    /// Signals an error that occurred during an agent run.
426    /// Contains error message and optional error code.
427    RunError(RunErrorEvent),
428
429    /// Signals the start of a step within an agent run.
430    /// Contains the name of the step being started.
431    StepStarted(StepStartedEvent),
432
433    /// Signals the completion of a step within an agent run.
434    /// Contains the name of the completed step.
435    StepFinished(StepFinishedEvent),
436}
437
438impl Event {
439    /// Get the event type
440    pub fn event_type(&self) -> EventType {
441        match self {
442            Event::TextMessageStart(_) => EventType::TextMessageStart,
443            Event::TextMessageContent(_) => EventType::TextMessageContent,
444            Event::TextMessageEnd(_) => EventType::TextMessageEnd,
445            Event::TextMessageChunk(_) => EventType::TextMessageChunk,
446            Event::ThinkingTextMessageStart(_) => EventType::ThinkingTextMessageStart,
447            Event::ThinkingTextMessageContent(_) => EventType::ThinkingTextMessageContent,
448            Event::ThinkingTextMessageEnd(_) => EventType::ThinkingTextMessageEnd,
449            Event::ToolCallStart(_) => EventType::ToolCallStart,
450            Event::ToolCallArgs(_) => EventType::ToolCallArgs,
451            Event::ToolCallEnd(_) => EventType::ToolCallEnd,
452            Event::ToolCallChunk(_) => EventType::ToolCallChunk,
453            Event::ToolCallResult(_) => EventType::ToolCallResult,
454            Event::ThinkingStart(_) => EventType::ThinkingStart,
455            Event::ThinkingEnd(_) => EventType::ThinkingEnd,
456            Event::StateSnapshot(_) => EventType::StateSnapshot,
457            Event::StateDelta(_) => EventType::StateDelta,
458            Event::MessagesSnapshot(_) => EventType::MessagesSnapshot,
459            Event::Raw(_) => EventType::Raw,
460            Event::Custom(_) => EventType::Custom,
461            Event::RunStarted(_) => EventType::RunStarted,
462            Event::RunFinished(_) => EventType::RunFinished,
463            Event::RunError(_) => EventType::RunError,
464            Event::StepStarted(_) => EventType::StepStarted,
465            Event::StepFinished(_) => EventType::StepFinished,
466        }
467    }
468
469    /// Get the timestamp if available
470    pub fn timestamp(&self) -> Option<f64> {
471        match self {
472            Event::TextMessageStart(e) => e.base.timestamp,
473            Event::TextMessageContent(e) => e.base.timestamp,
474            Event::TextMessageEnd(e) => e.base.timestamp,
475            Event::TextMessageChunk(e) => e.base.timestamp,
476            Event::ThinkingTextMessageStart(e) => e.base.timestamp,
477            Event::ThinkingTextMessageContent(e) => e.base.timestamp,
478            Event::ThinkingTextMessageEnd(e) => e.base.timestamp,
479            Event::ToolCallStart(e) => e.base.timestamp,
480            Event::ToolCallArgs(e) => e.base.timestamp,
481            Event::ToolCallEnd(e) => e.base.timestamp,
482            Event::ToolCallChunk(e) => e.base.timestamp,
483            Event::ToolCallResult(e) => e.base.timestamp,
484            Event::ThinkingStart(e) => e.base.timestamp,
485            Event::ThinkingEnd(e) => e.base.timestamp,
486            Event::StateSnapshot(e) => e.base.timestamp,
487            Event::StateDelta(e) => e.base.timestamp,
488            Event::MessagesSnapshot(e) => e.base.timestamp,
489            Event::Raw(e) => e.base.timestamp,
490            Event::Custom(e) => e.base.timestamp,
491            Event::RunStarted(e) => e.base.timestamp,
492            Event::RunFinished(e) => e.base.timestamp,
493            Event::RunError(e) => e.base.timestamp,
494            Event::StepStarted(e) => e.base.timestamp,
495            Event::StepFinished(e) => e.base.timestamp,
496        }
497    }
498}
499
500/// Validation error types for events in the Agent User Interaction Protocol.
501/// These errors represent validation failures when creating or processing events.
502#[derive(Debug, thiserror::Error)]
503pub enum EventValidationError {
504    #[error("Delta must not be an empty string")]
505    EmptyDelta,
506    #[error("Invalid event format: {0}")]
507    InvalidFormat(String),
508}
509
510/// Validate text message content event
511impl TextMessageContentEvent {
512    pub fn validate(&self) -> Result<(), EventValidationError> {
513        if self.delta.is_empty() {
514            return Err(EventValidationError::EmptyDelta);
515        }
516        Ok(())
517    }
518}
519
520/// Builder pattern for creating events
521impl TextMessageStartEvent {
522    pub fn new(message_id: impl Into<MessageId>) -> Self {
523        Self {
524            base: BaseEvent {
525                timestamp: None,
526                raw_event: None,
527            },
528            message_id: message_id.into(),
529            role: Role::Assistant,
530        }
531    }
532
533    pub fn with_timestamp(mut self, timestamp: f64) -> Self {
534        self.base.timestamp = Some(timestamp);
535        self
536    }
537
538    pub fn with_raw_event(mut self, raw_event: JsonValue) -> Self {
539        self.base.raw_event = Some(raw_event);
540        self
541    }
542}
543
544impl TextMessageContentEvent {
545    pub fn new(
546        message_id: impl Into<MessageId>,
547        delta: String,
548    ) -> Result<Self, EventValidationError> {
549        let event = Self {
550            base: BaseEvent {
551                timestamp: None,
552                raw_event: None,
553            },
554            message_id: message_id.into(),
555            delta,
556        };
557        event.validate()?;
558        Ok(event)
559    }
560
561    pub fn with_timestamp(mut self, timestamp: f64) -> Self {
562        self.base.timestamp = Some(timestamp);
563        self
564    }
565}