Skip to main content

systemprompt_models/agui/
payloads.rs

1//! Event payload types for the AG-UI streaming protocol.
2//!
3//! Each struct here is the body of one AG-UI event — run lifecycle
4//! (`RunStarted`/`RunFinished`/`RunError`), step boundaries, streaming text and
5//! tool-call deltas, state snapshots/deltas, and the [`CustomPayload`] envelope
6//! for application-defined events (artifacts, execution steps, loaded skills).
7//! Field casing follows the wire format via `rename_all = "camelCase"`.
8//!
9//! Copyright (c) systemprompt.io — Business Source License 1.1.
10//! See <https://systemprompt.io> for licensing details.
11
12use serde::{Deserialize, Serialize};
13use serde_json::Value;
14use systemprompt_identifiers::{AiToolCallId, ContextId, MessageId, TaskId};
15
16use super::JsonPatchOperation;
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
19#[serde(rename_all = "lowercase")]
20pub enum MessageRole {
21    User,
22    Assistant,
23    System,
24    Tool,
25}
26
27#[derive(Debug, Clone, Serialize, Deserialize)]
28#[serde(rename_all = "camelCase")]
29pub struct RunStartedPayload {
30    pub thread_id: ContextId,
31    pub run_id: TaskId,
32    #[serde(skip_serializing_if = "Option::is_none")]
33    pub input: Option<Value>,
34}
35
36#[derive(Debug, Clone, Serialize, Deserialize)]
37#[serde(rename_all = "camelCase")]
38pub struct RunFinishedPayload {
39    pub thread_id: ContextId,
40    pub run_id: TaskId,
41    #[serde(skip_serializing_if = "Option::is_none")]
42    pub result: Option<Value>,
43}
44
45#[derive(Debug, Clone, Serialize, Deserialize)]
46#[serde(rename_all = "camelCase")]
47pub struct RunErrorPayload {
48    pub message: String,
49    #[serde(skip_serializing_if = "Option::is_none")]
50    pub code: Option<String>,
51}
52
53#[derive(Debug, Clone, Serialize, Deserialize)]
54#[serde(rename_all = "camelCase")]
55pub struct StepStartedPayload {
56    pub step_name: String,
57}
58
59#[derive(Debug, Clone, Serialize, Deserialize)]
60#[serde(rename_all = "camelCase")]
61pub struct StepFinishedPayload {
62    pub step_name: String,
63}
64
65#[derive(Debug, Clone, Serialize, Deserialize)]
66#[serde(rename_all = "camelCase")]
67pub struct TextMessageStartPayload {
68    pub message_id: MessageId,
69    pub role: MessageRole,
70}
71
72#[derive(Debug, Clone, Serialize, Deserialize)]
73#[serde(rename_all = "camelCase")]
74pub struct TextMessageContentPayload {
75    pub message_id: MessageId,
76    pub delta: String,
77}
78
79#[derive(Debug, Clone, Serialize, Deserialize)]
80#[serde(rename_all = "camelCase")]
81pub struct TextMessageEndPayload {
82    pub message_id: MessageId,
83}
84
85#[derive(Debug, Clone, Serialize, Deserialize)]
86#[serde(rename_all = "camelCase")]
87pub struct ToolCallStartPayload {
88    pub tool_call_id: AiToolCallId,
89    pub tool_call_name: String,
90    #[serde(skip_serializing_if = "Option::is_none")]
91    pub parent_message_id: Option<MessageId>,
92}
93
94#[derive(Debug, Clone, Serialize, Deserialize)]
95#[serde(rename_all = "camelCase")]
96pub struct ToolCallArgsPayload {
97    pub tool_call_id: AiToolCallId,
98    pub delta: String,
99}
100
101#[derive(Debug, Clone, Serialize, Deserialize)]
102#[serde(rename_all = "camelCase")]
103pub struct ToolCallEndPayload {
104    pub tool_call_id: AiToolCallId,
105}
106
107#[derive(Debug, Clone, Serialize, Deserialize)]
108#[serde(rename_all = "camelCase")]
109pub struct ToolCallResultPayload {
110    pub message_id: MessageId,
111    pub tool_call_id: AiToolCallId,
112    pub content: Value,
113    pub role: MessageRole,
114}
115
116#[derive(Debug, Clone, Serialize, Deserialize)]
117#[serde(rename_all = "camelCase")]
118pub struct StateSnapshotPayload {
119    pub snapshot: Value,
120}
121
122#[derive(Debug, Clone, Serialize, Deserialize)]
123#[serde(rename_all = "camelCase")]
124pub struct StateDeltaPayload {
125    pub delta: Vec<JsonPatchOperation>,
126}
127
128#[derive(Debug, Clone, Serialize, Deserialize)]
129#[serde(rename_all = "camelCase")]
130pub struct MessagesSnapshotPayload {
131    pub messages: Vec<Value>,
132}
133
134#[derive(Debug, Clone, Serialize, Deserialize)]
135#[serde(rename_all = "camelCase")]
136pub struct ArtifactCustomPayload {
137    pub artifact: crate::a2a::Artifact,
138    pub task_id: TaskId,
139    pub context_id: ContextId,
140}
141
142#[derive(Debug, Clone, Serialize, Deserialize)]
143#[serde(rename_all = "camelCase")]
144pub struct ExecutionStepCustomPayload {
145    pub step: crate::execution::ExecutionStep,
146    pub context_id: ContextId,
147}
148
149#[derive(Debug, Clone, Serialize, Deserialize)]
150#[serde(rename_all = "camelCase")]
151pub struct SkillLoadedCustomPayload {
152    pub skill_id: systemprompt_identifiers::SkillId,
153    pub skill_name: String,
154    #[serde(skip_serializing_if = "Option::is_none")]
155    pub description: Option<String>,
156    #[serde(skip_serializing_if = "Option::is_none")]
157    pub task_id: Option<TaskId>,
158}
159
160#[derive(Debug, Clone, Serialize, Deserialize)]
161#[serde(rename_all = "camelCase")]
162pub struct GenericCustomPayload {
163    pub name: String,
164    pub value: Value,
165}
166
167#[derive(Debug, Clone, Serialize, Deserialize)]
168#[serde(tag = "name", content = "value", rename_all = "snake_case")]
169pub enum CustomPayload {
170    Artifact(Box<ArtifactCustomPayload>),
171    ExecutionStep(Box<ExecutionStepCustomPayload>),
172    SkillLoaded(SkillLoadedCustomPayload),
173    #[serde(untagged)]
174    Generic(GenericCustomPayload),
175}