Skip to main content

adk_server/a2a/
types.rs

1use serde::{Deserialize, Serialize};
2use serde_json::{Map, Value};
3
4#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5#[serde(rename_all = "lowercase")]
6pub enum Role {
7    User,
8    Agent,
9}
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
12pub struct FileContent {
13    #[serde(skip_serializing_if = "Option::is_none")]
14    pub name: Option<String>,
15    #[serde(skip_serializing_if = "Option::is_none", rename = "mimeType")]
16    pub mime_type: Option<String>,
17    #[serde(skip_serializing_if = "Option::is_none")]
18    pub bytes: Option<String>,
19    #[serde(skip_serializing_if = "Option::is_none")]
20    pub uri: Option<String>,
21}
22
23#[derive(Debug, Clone, Serialize, Deserialize)]
24#[serde(untagged)]
25pub enum Part {
26    Text {
27        text: String,
28        #[serde(skip_serializing_if = "Option::is_none")]
29        metadata: Option<Map<String, Value>>,
30    },
31    File {
32        file: FileContent,
33        #[serde(skip_serializing_if = "Option::is_none")]
34        metadata: Option<Map<String, Value>>,
35    },
36    Data {
37        data: Map<String, Value>,
38        #[serde(skip_serializing_if = "Option::is_none")]
39        metadata: Option<Map<String, Value>>,
40    },
41}
42
43impl Part {
44    pub fn text(text: String) -> Self {
45        Part::Text { text, metadata: None }
46    }
47
48    pub fn file(file: FileContent) -> Self {
49        Part::File { file, metadata: None }
50    }
51}
52
53#[derive(Debug, Clone, Serialize, Deserialize)]
54pub struct Message {
55    pub role: Role,
56    pub parts: Vec<Part>,
57    #[serde(skip_serializing_if = "Option::is_none")]
58    pub metadata: Option<Map<String, Value>>,
59    #[serde(rename = "messageId")]
60    pub message_id: String,
61    #[serde(skip_serializing_if = "Option::is_none", rename = "taskId")]
62    pub task_id: Option<String>,
63    #[serde(skip_serializing_if = "Option::is_none", rename = "contextId")]
64    pub context_id: Option<String>,
65}
66
67impl Message {
68    pub fn builder() -> MessageBuilder {
69        MessageBuilder::default()
70    }
71}
72
73#[derive(Default)]
74pub struct MessageBuilder {
75    role: Option<Role>,
76    parts: Vec<Part>,
77    metadata: Option<Map<String, Value>>,
78    message_id: Option<String>,
79    task_id: Option<String>,
80    context_id: Option<String>,
81}
82
83impl MessageBuilder {
84    pub fn role(mut self, role: Role) -> Self {
85        self.role = Some(role);
86        self
87    }
88
89    pub fn parts(mut self, parts: Vec<Part>) -> Self {
90        self.parts = parts;
91        self
92    }
93
94    pub fn metadata(mut self, metadata: Option<Map<String, Value>>) -> Self {
95        self.metadata = metadata;
96        self
97    }
98
99    pub fn message_id(mut self, id: String) -> Self {
100        self.message_id = Some(id);
101        self
102    }
103
104    pub fn build(self) -> Message {
105        Message {
106            role: self.role.unwrap_or(Role::User),
107            parts: self.parts,
108            metadata: self.metadata,
109            message_id: self.message_id.unwrap_or_else(|| uuid::Uuid::new_v4().to_string()),
110            task_id: self.task_id,
111            context_id: self.context_id,
112        }
113    }
114}
115
116#[derive(Debug, Clone, Serialize, Deserialize)]
117pub struct Artifact {
118    #[serde(rename = "artifactId")]
119    pub artifact_id: String,
120    #[serde(skip_serializing_if = "Option::is_none")]
121    pub name: Option<String>,
122    #[serde(skip_serializing_if = "Option::is_none")]
123    pub description: Option<String>,
124    pub parts: Vec<Part>,
125    #[serde(skip_serializing_if = "Option::is_none")]
126    pub metadata: Option<Map<String, Value>>,
127    #[serde(skip_serializing_if = "Option::is_none")]
128    pub extensions: Option<Vec<String>>,
129}
130
131#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
132#[serde(rename_all = "kebab-case")]
133pub enum TaskState {
134    Submitted,
135    Working,
136    InputRequired,
137    Completed,
138    Failed,
139    Canceled,
140}
141
142#[derive(Debug, Clone, Serialize, Deserialize)]
143pub struct TaskStatus {
144    pub state: TaskState,
145    #[serde(skip_serializing_if = "Option::is_none")]
146    pub message: Option<String>,
147}
148
149#[derive(Debug, Clone, Serialize, Deserialize)]
150pub struct TaskStatusUpdateEvent {
151    #[serde(rename = "taskId")]
152    pub task_id: String,
153    #[serde(skip_serializing_if = "Option::is_none", rename = "contextId")]
154    pub context_id: Option<String>,
155    pub status: TaskStatus,
156    #[serde(rename = "finalUpdate")]
157    pub final_update: bool,
158}
159
160#[derive(Debug, Clone, Serialize, Deserialize)]
161pub struct TaskArtifactUpdateEvent {
162    #[serde(rename = "taskId")]
163    pub task_id: String,
164    #[serde(skip_serializing_if = "Option::is_none", rename = "contextId")]
165    pub context_id: Option<String>,
166    pub artifact: Artifact,
167    pub append: bool,
168    #[serde(rename = "lastChunk")]
169    pub last_chunk: bool,
170}
171
172#[derive(Debug, Clone, Serialize, Deserialize)]
173#[serde(untagged)]
174pub enum UpdateEvent {
175    TaskStatusUpdate(TaskStatusUpdateEvent),
176    TaskArtifactUpdate(TaskArtifactUpdateEvent),
177}
178
179#[derive(Debug, Clone, Serialize, Deserialize)]
180pub struct AgentSkill {
181    pub id: String,
182    pub name: String,
183    pub description: String,
184    pub tags: Vec<String>,
185    #[serde(skip_serializing_if = "Option::is_none")]
186    pub examples: Option<Vec<String>>,
187}
188
189impl AgentSkill {
190    pub fn new(id: String, name: String, description: String, tags: Vec<String>) -> Self {
191        Self { id, name, description, tags, examples: None }
192    }
193}
194
195#[derive(Debug, Clone, Serialize, Deserialize)]
196pub struct AgentCapabilities {
197    pub streaming: bool,
198    #[serde(rename = "pushNotifications")]
199    pub push_notifications: bool,
200    #[serde(rename = "stateTransitionHistory")]
201    pub state_transition_history: bool,
202    #[serde(skip_serializing_if = "Option::is_none")]
203    pub extensions: Option<Vec<String>>,
204}
205
206#[derive(Debug, Clone, Serialize, Deserialize)]
207pub struct AgentCard {
208    pub name: String,
209    pub description: String,
210    pub url: String,
211    pub version: String,
212    #[serde(rename = "protocolVersion")]
213    pub protocol_version: String,
214    pub capabilities: AgentCapabilities,
215    pub skills: Vec<AgentSkill>,
216}
217
218impl AgentCard {
219    pub fn builder() -> AgentCardBuilder {
220        AgentCardBuilder::default()
221    }
222}
223
224#[derive(Default)]
225pub struct AgentCardBuilder {
226    name: Option<String>,
227    description: Option<String>,
228    url: Option<String>,
229    version: Option<String>,
230    capabilities: Option<AgentCapabilities>,
231    skills: Vec<AgentSkill>,
232}
233
234impl AgentCardBuilder {
235    pub fn name(mut self, name: String) -> Self {
236        self.name = Some(name);
237        self
238    }
239
240    pub fn description(mut self, description: String) -> Self {
241        self.description = Some(description);
242        self
243    }
244
245    pub fn url(mut self, url: String) -> Self {
246        self.url = Some(url);
247        self
248    }
249
250    pub fn version(mut self, version: String) -> Self {
251        self.version = Some(version);
252        self
253    }
254
255    pub fn capabilities(mut self, capabilities: AgentCapabilities) -> Self {
256        self.capabilities = Some(capabilities);
257        self
258    }
259
260    pub fn skills(mut self, skills: Vec<AgentSkill>) -> Self {
261        self.skills = skills;
262        self
263    }
264
265    /// Build the `AgentCard`, returning an error if required fields are missing.
266    ///
267    /// # Errors
268    ///
269    /// Returns a descriptive error string when `name`, `description`, or `url`
270    /// have not been set (or were set to empty strings).
271    pub fn build(self) -> std::result::Result<AgentCard, String> {
272        let name =
273            self.name.filter(|s| !s.is_empty()).ok_or("AgentCard requires a non-empty 'name'")?;
274        let description = self
275            .description
276            .filter(|s| !s.is_empty())
277            .ok_or("AgentCard requires a non-empty 'description'")?;
278        let url =
279            self.url.filter(|s| !s.is_empty()).ok_or("AgentCard requires a non-empty 'url'")?;
280
281        Ok(AgentCard {
282            name,
283            description,
284            url,
285            version: self.version.unwrap_or_else(|| "1.0.0".to_string()),
286            protocol_version: "0.3.0".to_string(),
287            capabilities: self.capabilities.unwrap_or(AgentCapabilities {
288                streaming: false,
289                push_notifications: false,
290                state_transition_history: false,
291                extensions: None,
292            }),
293            skills: self.skills,
294        })
295    }
296}