systemprompt_models/a2a/
message.rs1use serde::{Deserialize, Serialize};
7use systemprompt_identifiers::{ContextId, MessageId, TaskId};
8
9#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
10#[serde(rename_all = "camelCase")]
11#[expect(
12 clippy::struct_field_names,
13 reason = "A2A wire schema names these fields message_id/task_id/context_id — public protocol \
14 vocabulary"
15)]
16pub struct Message {
17 pub role: MessageRole,
18 pub parts: Vec<Part>,
19 pub message_id: MessageId,
20 #[serde(skip_serializing_if = "Option::is_none")]
21 pub task_id: Option<TaskId>,
22 pub context_id: ContextId,
23 pub metadata: Option<serde_json::Value>,
25 pub extensions: Option<Vec<String>>,
26 #[serde(skip_serializing_if = "Option::is_none")]
27 pub reference_task_ids: Option<Vec<TaskId>>,
28}
29
30#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
31pub enum MessageRole {
32 #[serde(rename = "ROLE_USER")]
33 User,
34 #[serde(rename = "ROLE_AGENT")]
35 Agent,
36}
37
38#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
39#[serde(untagged)]
40pub enum Part {
41 Text(TextPart),
42 File(FilePart),
43 Data(DataPart),
44}
45
46#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
47pub struct TextPart {
48 pub text: String,
49}
50
51#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
52pub struct DataPart {
53 pub data: serde_json::Map<String, serde_json::Value>,
55}
56
57#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
58pub struct FilePart {
59 pub file: FileContent,
60}
61
62#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
63#[serde(rename_all = "camelCase")]
64pub struct FileContent {
65 pub name: Option<String>,
66 pub mime_type: Option<String>,
67 #[serde(skip_serializing_if = "Option::is_none")]
68 pub bytes: Option<String>,
69 #[serde(skip_serializing_if = "Option::is_none")]
70 pub url: Option<String>,
71}
72
73impl Part {
74 pub fn as_text(&self) -> Option<&str> {
75 match self {
76 Self::Text(text_part) => Some(&text_part.text),
77 _ => None,
78 }
79 }
80
81 pub fn as_data(&self) -> Option<serde_json::Value> {
83 match self {
84 Self::Data(data_part) => Some(serde_json::Value::Object(data_part.data.clone())),
85 _ => None,
86 }
87 }
88
89 pub fn as_file(&self) -> Option<serde_json::Value> {
91 match self {
92 Self::File(file_part) => serde_json::to_value(&file_part.file).ok(),
93 _ => None,
94 }
95 }
96}