Skip to main content

brainwires_a2a/
types.rs

1//! Core A2A message types: Message, Part, Artifact, Role.
2
3use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5
6/// Sender role in A2A communication.
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
8pub enum Role {
9    /// Client-to-server message.
10    #[serde(rename = "ROLE_USER")]
11    User,
12    /// Server-to-client message.
13    #[serde(rename = "ROLE_AGENT")]
14    Agent,
15    /// Unspecified role.
16    #[serde(rename = "ROLE_UNSPECIFIED")]
17    Unspecified,
18}
19
20/// A single unit of communication content.
21///
22/// Exactly one of `text`, `raw`, `url`, or `data` must be set.
23#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
24pub struct Part {
25    /// Text content.
26    #[serde(skip_serializing_if = "Option::is_none")]
27    pub text: Option<String>,
28    /// Base64-encoded raw bytes.
29    #[serde(skip_serializing_if = "Option::is_none")]
30    pub raw: Option<String>,
31    /// URL reference.
32    #[serde(skip_serializing_if = "Option::is_none")]
33    pub url: Option<String>,
34    /// Structured JSON data.
35    #[serde(skip_serializing_if = "Option::is_none")]
36    pub data: Option<serde_json::Value>,
37    /// MIME type of the content.
38    #[serde(skip_serializing_if = "Option::is_none", rename = "mediaType")]
39    pub media_type: Option<String>,
40    /// File name.
41    #[serde(skip_serializing_if = "Option::is_none")]
42    pub filename: Option<String>,
43    /// Custom metadata.
44    #[serde(skip_serializing_if = "Option::is_none")]
45    pub metadata: Option<HashMap<String, serde_json::Value>>,
46}
47
48/// A single communication message between client and server.
49#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
50pub struct Message {
51    /// Unique message identifier.
52    #[serde(rename = "messageId")]
53    pub message_id: String,
54    /// Sender role.
55    pub role: Role,
56    /// Content parts.
57    pub parts: Vec<Part>,
58    /// Context identifier (conversation/session).
59    #[serde(rename = "contextId", skip_serializing_if = "Option::is_none")]
60    pub context_id: Option<String>,
61    /// Associated task identifier.
62    #[serde(rename = "taskId", skip_serializing_if = "Option::is_none")]
63    pub task_id: Option<String>,
64    /// Referenced task identifiers for additional context.
65    #[serde(rename = "referenceTaskIds", skip_serializing_if = "Option::is_none")]
66    pub reference_task_ids: Option<Vec<String>>,
67    /// Custom metadata.
68    #[serde(skip_serializing_if = "Option::is_none")]
69    pub metadata: Option<HashMap<String, serde_json::Value>>,
70    /// Extension URIs present in this message.
71    #[serde(skip_serializing_if = "Option::is_none")]
72    pub extensions: Option<Vec<String>>,
73}
74
75impl Message {
76    /// Create a new user message with text content.
77    pub fn user_text(text: impl Into<String>) -> Self {
78        Self {
79            message_id: uuid::Uuid::new_v4().to_string(),
80            role: Role::User,
81            parts: vec![Part {
82                text: Some(text.into()),
83                raw: None,
84                url: None,
85                data: None,
86                media_type: None,
87                filename: None,
88                metadata: None,
89            }],
90            context_id: None,
91            task_id: None,
92            reference_task_ids: None,
93            metadata: None,
94            extensions: None,
95        }
96    }
97
98    /// Create a new agent message with text content.
99    pub fn agent_text(text: impl Into<String>) -> Self {
100        let mut msg = Self::user_text(text);
101        msg.role = Role::Agent;
102        msg
103    }
104}
105
106/// Task output artifact.
107#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
108pub struct Artifact {
109    /// Unique artifact identifier (unique within a task).
110    #[serde(rename = "artifactId")]
111    pub artifact_id: String,
112    /// Human-readable name.
113    #[serde(skip_serializing_if = "Option::is_none")]
114    pub name: Option<String>,
115    /// Human-readable description.
116    #[serde(skip_serializing_if = "Option::is_none")]
117    pub description: Option<String>,
118    /// Artifact content parts.
119    pub parts: Vec<Part>,
120    /// Custom metadata.
121    #[serde(skip_serializing_if = "Option::is_none")]
122    pub metadata: Option<HashMap<String, serde_json::Value>>,
123    /// Extension URIs.
124    #[serde(skip_serializing_if = "Option::is_none")]
125    pub extensions: Option<Vec<String>>,
126}