pub struct Message {
pub id: MessageId,
pub role: MessageRole,
pub parts: Vec<Part>,
pub task_id: Option<TaskId>,
pub context_id: Option<ContextId>,
pub reference_task_ids: Option<Vec<TaskId>>,
pub extensions: Option<Vec<String>>,
pub metadata: Option<Value>,
}Expand description
A message exchanged between a client and an agent.
Fields§
§id: MessageIdUnique message identifier.
role: MessageRoleRole of the message originator.
parts: Vec<Part>Message content parts.
Spec requirement: Must contain at least one element. The A2A protocol does not define behavior for empty parts lists.
Parsing treats absence as empty (ProtoJSON printers omit empty
repeated fields; the gRPC binding already decodes it that way) — the
server rejects empty parts with a structured invalid-params error
instead of a JSON type error, uniformly across bindings.
task_id: Option<TaskId>Task this message belongs to, if any.
context_id: Option<ContextId>Conversation context this message belongs to, if any.
reference_task_ids: Option<Vec<TaskId>>IDs of tasks referenced by this message.
extensions: Option<Vec<String>>URIs of extensions used in this message.
metadata: Option<Value>Arbitrary metadata.
Implementations§
Source§impl Message
impl Message
Sourcepub fn new(
id: impl Into<MessageId>,
role: MessageRole,
parts: Vec<Part>,
) -> Message
pub fn new( id: impl Into<MessageId>, role: MessageRole, parts: Vec<Part>, ) -> Message
Creates a message with the given id, role and parts.
The five optional fields start as None; set the ones you need with
the with_* methods below. Before these existed the only way to make
a Message was a struct literal naming all eight fields, and this
workspace contained 105 of them — a message carrying one line of text
cost ten lines to write, five of which said None.
The id is a parameter rather than something this constructor invents,
for the same reason Artifact::new
takes one: this crate depends on serde and serde_json and nothing
else, so it has no random source to mint one from, and a
clock-derived id is not unique under concurrency. Servers in this
workspace use uuid::Uuid::new_v4(); a client that already has a
correlation id of its own should use that.
use a2a_protocol_types::message::{Message, MessageRole, Part};
let msg = Message::new("m1", MessageRole::User, vec![Part::text("hello")]);
assert_eq!(msg.text(), Some("hello"));
assert_eq!(msg.role, MessageRole::User);Sourcepub fn user(id: impl Into<MessageId>, parts: Vec<Part>) -> Message
pub fn user(id: impl Into<MessageId>, parts: Vec<Part>) -> Message
Creates a MessageRole::User message with the given parts.
use a2a_protocol_types::message::{Message, MessageRole, Part};
let msg = Message::user("m1", vec![Part::url("https://example.com/f.pdf")]);
assert_eq!(msg.role, MessageRole::User);Sourcepub fn agent(id: impl Into<MessageId>, parts: Vec<Part>) -> Message
pub fn agent(id: impl Into<MessageId>, parts: Vec<Part>) -> Message
Creates a MessageRole::Agent message with the given parts.
use a2a_protocol_types::message::{Message, MessageRole, Part};
let msg = Message::agent("m1", vec![Part::text("done")]);
assert_eq!(msg.role, MessageRole::Agent);Sourcepub fn user_text(id: impl Into<MessageId>, text: impl Into<String>) -> Message
pub fn user_text(id: impl Into<MessageId>, text: impl Into<String>) -> Message
Creates a MessageRole::User message carrying one text part.
The shape almost every caller wants: send this text to an agent.
use a2a_protocol_types::message::Message;
let msg = Message::user_text("m1", "what is the status of order 42?");
assert_eq!(msg.text(), Some("what is the status of order 42?"));
assert_eq!(msg.parts.len(), 1);Sourcepub fn agent_text(id: impl Into<MessageId>, text: impl Into<String>) -> Message
pub fn agent_text(id: impl Into<MessageId>, text: impl Into<String>) -> Message
Creates a MessageRole::Agent message carrying one text part.
use a2a_protocol_types::message::Message;
let msg = Message::agent_text("m2", "order 42 shipped on Tuesday");
assert_eq!(msg.text(), Some("order 42 shipped on Tuesday"));Sourcepub fn with_task_id(self, task_id: impl Into<TaskId>) -> Message
pub fn with_task_id(self, task_id: impl Into<TaskId>) -> Message
Attaches this message to an existing task.
Sending a follow-up without this is what makes it a new task on the same context rather than a continuation of the old one.
use a2a_protocol_types::message::Message;
use a2a_protocol_types::task::TaskId;
let msg = Message::user_text("m2", "and the one after that?")
.with_task_id("task-1");
assert_eq!(msg.task_id, Some(TaskId::new("task-1")));Sourcepub fn with_context_id(self, context_id: impl Into<ContextId>) -> Message
pub fn with_context_id(self, context_id: impl Into<ContextId>) -> Message
Places this message in an existing conversation context.
use a2a_protocol_types::message::Message;
use a2a_protocol_types::task::ContextId;
let msg = Message::user_text("m2", "go on").with_context_id("ctx-1");
assert_eq!(msg.context_id, Some(ContextId::new("ctx-1")));Sourcepub fn with_reference_task_ids<I, T>(self, task_ids: I) -> Message
pub fn with_reference_task_ids<I, T>(self, task_ids: I) -> Message
Names earlier tasks this message refers to.
use a2a_protocol_types::message::Message;
let msg = Message::user_text("m3", "compare those two")
.with_reference_task_ids(["task-1", "task-2"]);
assert_eq!(msg.reference_task_ids.map(|ids| ids.len()), Some(2));Sourcepub fn with_extensions<I, S>(self, extensions: I) -> Message
pub fn with_extensions<I, S>(self, extensions: I) -> Message
Declares the extension URIs this message activates.
use a2a_protocol_types::message::Message;
let msg = Message::user_text("m1", "hi")
.with_extensions(["https://example.com/ext/v1"]);
assert_eq!(msg.extensions.as_deref(), Some(&["https://example.com/ext/v1".to_owned()][..]));Sourcepub fn with_metadata(self, metadata: Value) -> Message
pub fn with_metadata(self, metadata: Value) -> Message
Attaches opaque metadata to this message.
use a2a_protocol_types::message::Message;
let msg = Message::user_text("m1", "hi")
.with_metadata(serde_json::json!({ "skill": "triage" }));
assert_eq!(msg.metadata.as_ref().and_then(|m| m["skill"].as_str()), Some("triage"));Sourcepub fn text(&self) -> Option<&str>
pub fn text(&self) -> Option<&str>
Returns the text of the first text Part, or None if this message
carries no text part.
Reading the caller’s text is the most common operation in agent code.
This is the one-liner form of the parts.iter().find_map(..) match over
PartContent::Text:
use a2a_protocol_types::message::{Message, Part};
let msg = Message::user(
"m1",
vec![Part::url("https://example.com/f.pdf"), Part::text("hello")],
);
assert_eq!(msg.text(), Some("hello"));Non-text parts are skipped rather than ending the search, so a message that leads with a file attachment still yields its text.
Trait Implementations§
Source§impl<'de> Deserialize<'de> for Message
impl<'de> Deserialize<'de> for Message
Source§fn deserialize<__D>(
__deserializer: __D,
) -> Result<Message, <__D as Deserializer<'de>>::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(
__deserializer: __D,
) -> Result<Message, <__D as Deserializer<'de>>::Error>where
__D: Deserializer<'de>,
impl Eq for Message
Source§impl Serialize for Message
impl Serialize for Message
Source§fn serialize<__S>(
&self,
__serializer: __S,
) -> Result<<__S as Serializer>::Ok, <__S as Serializer>::Error>where
__S: Serializer,
fn serialize<__S>(
&self,
__serializer: __S,
) -> Result<<__S as Serializer>::Ok, <__S as Serializer>::Error>where
__S: Serializer,
impl StructuralPartialEq for Message
Auto Trait Implementations§
impl Freeze for Message
impl RefUnwindSafe for Message
impl Send for Message
impl Sync for Message
impl Unpin for Message
impl UnsafeUnpin for Message
impl UnwindSafe for Message
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> DeserializeOwned for Twhere
T: for<'de> Deserialize<'de>,
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key and return true if they are equal.Source§impl<T> FutureExt for T
impl<T> FutureExt for T
Source§fn with_context(self, otel_cx: Context) -> WithContext<Self> ⓘ
fn with_context(self, otel_cx: Context) -> WithContext<Self> ⓘ
Source§fn with_current_context(self) -> WithContext<Self> ⓘ
fn with_current_context(self) -> WithContext<Self> ⓘ
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> IntoRequest<T> for T
impl<T> IntoRequest<T> for T
Source§fn into_request(self) -> Request<T>
fn into_request(self) -> Request<T>
T in a tonic::Request