// Who sent a message, and the message itself.
///|
/// The sender of a message (A2A `Role`).
#derive(Eq)
pub(all) enum Role {
Unspecified
User
Agent
}
///|
/// The wire name of the role, as ProtoJSON writes an enum.
pub fn Role::wire(self : Role) -> String {
match self {
Unspecified => "ROLE_UNSPECIFIED"
User => "ROLE_USER"
Agent => "ROLE_AGENT"
}
}
///|
/// The role named by its wire form. Both the proto name and the bare word are read, because
/// clients written against earlier drafts send the bare word.
pub fn Role::from_wire(name : String) -> Role {
match name {
"ROLE_USER" | "user" => User
"ROLE_AGENT" | "agent" => Agent
_ => Unspecified
}
}
///|
pub impl @json.ToJson for Role with fn to_json(self) {
Json::string(self.wire())
}
///|
pub impl @json.FromJson for Role with fn from_json(json, path) {
match json {
String(name) => Role::from_wire(name)
_ => raise @json.JsonDecodeError((path, "want a role name"))
}
}
///|
/// One turn of a conversation between a client and an agent (A2A `Message`).
#derive(Eq)
pub(all) struct Message {
message_id : String
role : Role
parts : Array[Part]
context_id : String?
task_id : String?
reference_task_ids : Array[String]?
extensions : Array[String]?
metadata : Json?
}
///|
/// A message from the client, carrying one text part.
pub fn Message::user(message_id : String, text : String) -> Message {
Message::new(message_id, User, [Part::text(text)])
}
///|
/// A message from the agent, carrying one text part.
pub fn Message::agent(message_id : String, text : String) -> Message {
Message::new(message_id, Agent, [Part::text(text)])
}
///|
/// A message from `role` carrying `parts`.
pub fn Message::new(
message_id : String,
role : Role,
parts : Array[Part],
) -> Message {
{
message_id,
role,
parts,
context_id: None,
task_id: None,
reference_task_ids: None,
extensions: None,
metadata: None,
}
}
///|
/// The same message, continuing the task and context given.
pub fn Message::in_task(
self : Message,
task_id : String,
context_id : String,
) -> Message {
{ ..self, task_id: Some(task_id), context_id: Some(context_id), }
}
///|
/// The text of every text part, in order and with nothing added: a part is content, so joining
/// must not invent characters between them.
pub fn Message::text(self : Message) -> String {
let texts : Array[String] = []
for part in self.parts {
guard part.content is Text(text) else { continue }
texts.push(text)
}
texts.join("")
}
///|
pub impl @json.ToJson for Message with fn to_json(self) {
let fields = object()
fields["messageId"] = Json::string(self.message_id)
fields["role"] = self.role.to_json()
fields["parts"] = Json::array(self.parts.map(Part::to_json))
put_str(fields, "contextId", self.context_id)
put_str(fields, "taskId", self.task_id)
put_strs(fields, "referenceTaskIds", self.reference_task_ids)
put_strs(fields, "extensions", self.extensions)
put_json(fields, "metadata", self.metadata)
Json::object(fields)
}
///|
pub impl @json.FromJson for Message with fn from_json(json, path) {
let fields = as_object(json, path)
let role = match fields.get("role") {
Some(String(name)) => Role::from_wire(name)
_ => raise wrong_type(path, "role", "a role name")
}
{
message_id: need_str(fields, "messageId", path),
role,
parts: read_array(fields, "parts", path, Part::from_json),
context_id: opt_str(fields, "contextId"),
task_id: opt_str(fields, "taskId"),
reference_task_ids: opt_strs(fields, "referenceTaskIds"),
extensions: opt_strs(fields, "extensions"),
metadata: fields.get("metadata"),
}
}
///|
pub extend Role with @json.ToJson::{to_json}
///|
pub extend Message with @json.ToJson::{to_json}
///|
pub extend Role with @json.FromJson::{from_json}
///|
pub extend Message with @json.FromJson::{from_json}
///|
pub extend Role with Eq::{not_equal, equal}
///|
pub extend Message with Eq::{not_equal, equal}