strands_agents/multiagent/a2a/
types.rs1use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
7#[serde(rename_all = "lowercase")]
8pub enum A2ATaskState {
9 Pending,
10 Working,
11 Completed,
12 Failed,
13 Cancelled,
14}
15
16#[derive(Debug, Clone, Serialize, Deserialize)]
18#[serde(rename_all = "camelCase")]
19pub struct A2ATask {
20 pub id: String,
21 pub context_id: String,
22 pub state: A2ATaskState,
23 #[serde(skip_serializing_if = "Option::is_none")]
24 pub message: Option<A2AMessage>,
25 #[serde(skip_serializing_if = "Option::is_none")]
26 pub artifacts: Option<Vec<A2AArtifact>>,
27}
28
29impl A2ATask {
30 pub fn new(id: impl Into<String>, context_id: impl Into<String>) -> Self {
31 Self {
32 id: id.into(),
33 context_id: context_id.into(),
34 state: A2ATaskState::Pending,
35 message: None,
36 artifacts: None,
37 }
38 }
39}
40
41#[derive(Debug, Clone, Serialize, Deserialize)]
43#[serde(rename_all = "camelCase")]
44pub struct A2AArtifact {
45 pub name: String,
46 pub parts: Vec<A2APart>,
47 #[serde(skip_serializing_if = "Option::is_none")]
48 pub index: Option<usize>,
49}
50
51#[derive(Debug, Clone, Serialize, Deserialize)]
53#[serde(rename_all = "camelCase")]
54pub struct A2AMessage {
55 pub role: String,
56 pub parts: Vec<A2APart>,
57 #[serde(skip_serializing_if = "Option::is_none")]
58 pub context_id: Option<String>,
59 #[serde(skip_serializing_if = "Option::is_none")]
60 pub task_id: Option<String>,
61}
62
63impl A2AMessage {
64 pub fn user(parts: Vec<A2APart>) -> Self {
65 Self {
66 role: "user".to_string(),
67 parts,
68 context_id: None,
69 task_id: None,
70 }
71 }
72
73 pub fn agent(parts: Vec<A2APart>, context_id: Option<String>, task_id: Option<String>) -> Self {
74 Self {
75 role: "agent".to_string(),
76 parts,
77 context_id,
78 task_id,
79 }
80 }
81}
82
83#[derive(Debug, Clone, Serialize, Deserialize)]
85#[serde(tag = "type", rename_all = "lowercase")]
86pub enum A2APart {
87 Text { text: String },
88 File { file: A2AFile },
89 Data { data: serde_json::Value },
90}
91
92impl A2APart {
93 pub fn text(text: impl Into<String>) -> Self {
94 Self::Text { text: text.into() }
95 }
96
97 pub fn data(data: serde_json::Value) -> Self {
98 Self::Data { data }
99 }
100}
101
102#[derive(Debug, Clone, Serialize, Deserialize)]
104#[serde(rename_all = "camelCase")]
105pub struct A2AFile {
106 pub name: String,
107 #[serde(skip_serializing_if = "Option::is_none")]
108 pub mime_type: Option<String>,
109 #[serde(skip_serializing_if = "Option::is_none")]
110 pub bytes: Option<String>,
111 #[serde(skip_serializing_if = "Option::is_none")]
112 pub uri: Option<String>,
113}
114
115#[derive(Debug, Clone, Serialize, Deserialize)]
117#[serde(rename_all = "camelCase")]
118pub struct A2ARequest {
119 pub jsonrpc: String,
120 pub id: serde_json::Value,
121 pub method: String,
122 #[serde(skip_serializing_if = "Option::is_none")]
123 pub params: Option<serde_json::Value>,
124}
125
126impl A2ARequest {
127 pub fn new(id: impl Into<serde_json::Value>, method: impl Into<String>) -> Self {
128 Self {
129 jsonrpc: "2.0".to_string(),
130 id: id.into(),
131 method: method.into(),
132 params: None,
133 }
134 }
135
136 pub fn with_params(mut self, params: serde_json::Value) -> Self {
137 self.params = Some(params);
138 self
139 }
140}
141
142#[derive(Debug, Clone, Serialize, Deserialize)]
144#[serde(rename_all = "camelCase")]
145pub struct A2AResponse {
146 pub jsonrpc: String,
147 pub id: serde_json::Value,
148 #[serde(skip_serializing_if = "Option::is_none")]
149 pub result: Option<serde_json::Value>,
150 #[serde(skip_serializing_if = "Option::is_none")]
151 pub error: Option<A2AError>,
152}
153
154impl A2AResponse {
155 pub fn success(id: serde_json::Value, result: serde_json::Value) -> Self {
156 Self {
157 jsonrpc: "2.0".to_string(),
158 id,
159 result: Some(result),
160 error: None,
161 }
162 }
163
164 pub fn error(id: serde_json::Value, error: A2AError) -> Self {
165 Self {
166 jsonrpc: "2.0".to_string(),
167 id,
168 result: None,
169 error: Some(error),
170 }
171 }
172}
173
174#[derive(Debug, Clone, Serialize, Deserialize)]
176pub struct A2AError {
177 pub code: i32,
178 pub message: String,
179 #[serde(skip_serializing_if = "Option::is_none")]
180 pub data: Option<serde_json::Value>,
181}
182
183impl A2AError {
184 pub fn internal(message: impl Into<String>) -> Self {
185 Self {
186 code: -32603,
187 message: message.into(),
188 data: None,
189 }
190 }
191
192 pub fn invalid_request(message: impl Into<String>) -> Self {
193 Self {
194 code: -32600,
195 message: message.into(),
196 data: None,
197 }
198 }
199
200 pub fn method_not_found(message: impl Into<String>) -> Self {
201 Self {
202 code: -32601,
203 message: message.into(),
204 data: None,
205 }
206 }
207}
208
209#[derive(Debug, Clone, Default, Serialize, Deserialize)]
211pub struct A2ACapabilities {
212 #[serde(default)]
213 pub streaming: bool,
214 #[serde(default)]
215 pub push_notifications: bool,
216 #[serde(default)]
217 pub state_transition_history: bool,
218}
219
220#[derive(Debug, Clone, Serialize, Deserialize)]
222pub struct AgentSkill {
223 pub id: String,
224 pub name: String,
225 #[serde(skip_serializing_if = "Option::is_none")]
226 pub description: Option<String>,
227 #[serde(skip_serializing_if = "Option::is_none")]
228 pub tags: Option<Vec<String>>,
229 #[serde(skip_serializing_if = "Option::is_none")]
230 pub examples: Option<Vec<String>>,
231}
232
233impl AgentSkill {
234 pub fn new(id: impl Into<String>, name: impl Into<String>) -> Self {
235 Self {
236 id: id.into(),
237 name: name.into(),
238 description: None,
239 tags: None,
240 examples: None,
241 }
242 }
243
244 pub fn with_description(mut self, description: impl Into<String>) -> Self {
245 self.description = Some(description.into());
246 self
247 }
248}
249
250#[derive(Debug, Clone, Serialize, Deserialize)]
252#[serde(rename_all = "camelCase")]
253pub struct AgentCard {
254 pub name: String,
255 #[serde(skip_serializing_if = "Option::is_none")]
256 pub description: Option<String>,
257 pub url: String,
258 pub version: String,
259 pub capabilities: A2ACapabilities,
260 #[serde(skip_serializing_if = "Option::is_none")]
261 pub skills: Option<Vec<AgentSkill>>,
262 #[serde(skip_serializing_if = "Option::is_none")]
263 pub default_input_modes: Option<Vec<String>>,
264 #[serde(skip_serializing_if = "Option::is_none")]
265 pub default_output_modes: Option<Vec<String>>,
266}
267
268impl AgentCard {
269 pub fn new(
270 name: impl Into<String>,
271 url: impl Into<String>,
272 version: impl Into<String>,
273 ) -> Self {
274 Self {
275 name: name.into(),
276 description: None,
277 url: url.into(),
278 version: version.into(),
279 capabilities: A2ACapabilities::default(),
280 skills: None,
281 default_input_modes: None,
282 default_output_modes: None,
283 }
284 }
285
286 pub fn with_description(mut self, description: impl Into<String>) -> Self {
287 self.description = Some(description.into());
288 self
289 }
290
291 pub fn with_streaming(mut self, streaming: bool) -> Self {
292 self.capabilities.streaming = streaming;
293 self
294 }
295
296 pub fn with_skills(mut self, skills: Vec<AgentSkill>) -> Self {
297 self.skills = Some(skills);
298 self
299 }
300}
301