// The methods an A2A endpoint answers, and the request and reply objects that travel with them.
///|
/// Every method of the JSON-RPC binding.
#derive(Eq)
pub(all) enum Method {
SendMessage
SendStreamingMessage
GetTask
ListTasks
CancelTask
SubscribeToTask
CreatePushConfig
GetPushConfig
ListPushConfigs
DeletePushConfig
GetExtendedCard
}
///|
/// The name the method goes by on the wire: PascalCase, as section 9.1 requires.
pub fn Method::name(self : Method) -> String {
match self {
SendMessage => "SendMessage"
SendStreamingMessage => "SendStreamingMessage"
GetTask => "GetTask"
ListTasks => "ListTasks"
CancelTask => "CancelTask"
SubscribeToTask => "SubscribeToTask"
CreatePushConfig => "CreateTaskPushNotificationConfig"
GetPushConfig => "GetTaskPushNotificationConfig"
ListPushConfigs => "ListTaskPushNotificationConfigs"
DeletePushConfig => "DeleteTaskPushNotificationConfig"
GetExtendedCard => "GetExtendedAgentCard"
}
}
///|
/// The method that goes by `name`, when it is one of the protocol's.
pub fn Method::from_name(name : String) -> Method? {
for
candidate in [
SendMessage,
SendStreamingMessage,
GetTask,
ListTasks,
CancelTask,
SubscribeToTask,
CreatePushConfig,
GetPushConfig,
ListPushConfigs,
DeletePushConfig,
GetExtendedCard,
] {
if candidate.name() == name {
return Some(candidate)
}
}
None
}
///|
/// Whether the answer to this method is a stream of events rather than a single body.
pub fn Method::streams(self : Method) -> Bool {
match self {
SendStreamingMessage | SubscribeToTask => true
_ => false
}
}
///|
/// Whether `name` is one of the methods whose answer is a stream, for a transport that has to
/// decide before reading the whole request.
pub fn is_streaming_method(name : String) -> Bool {
match Method::from_name(name) {
Some(candidate) => candidate.streams()
None => false
}
}
///|
/// The parameters of a call as an object. A call with no parameters reads as an empty one, so a
/// method that needs nothing can still be called.
fn params_object(params : Json?) -> Map[String, Json] raise @moonjrpc.Failed {
match params {
Some(Object(fields)) => fields
None => Map([])
_ => raise InvalidParams("params must be an object").as_failed()
}
}
///|
/// Read a request object out of a call's parameters. Parameters that do not decode are the
/// caller's mistake, so they come back as `InvalidParams` naming the path that would not read.
fn[A : @json.FromJson] decode(params : Json?) -> A raise @moonjrpc.Failed {
let json = params.unwrap_or(Json::null())
@json.from_json(json) catch {
@json.JsonDecodeError((path, reason)) =>
raise InvalidParams("\{path}: \{reason}").as_failed()
}
}
///|
/// How a send request should be handled: whether to wait, how much history to bring back, and what
/// the caller is willing to receive.
#derive(Eq)
pub(all) struct SendMessageConfig {
return_immediately : Bool
history_length : Int32?
accepted_output_modes : Array[String]
push : TaskPushConfig?
}
///|
/// Wait for the task to finish, and bring back its whole history.
pub fn SendMessageConfig::waiting() -> SendMessageConfig {
{
return_immediately: false,
history_length: None,
accepted_output_modes: [],
push: None,
}
}
///|
/// The same configuration, returning before the task finishes.
pub fn SendMessageConfig::immediately(
self : SendMessageConfig,
) -> SendMessageConfig {
{ ..self, return_immediately: true, }
}
///|
/// The same configuration, bringing back at most `history_length` messages.
pub fn SendMessageConfig::at_most(
self : SendMessageConfig,
history_length : Int32,
) -> SendMessageConfig {
{ ..self, history_length: Some(history_length), }
}
///|
/// The same configuration, registering `push` for the task it creates.
pub fn SendMessageConfig::pushing(
self : SendMessageConfig,
push : TaskPushConfig,
) -> SendMessageConfig {
{ ..self, push: Some(push), }
}
///|
/// Everything a send request carries (A2A `SendMessageRequest`).
#derive(Eq)
pub(all) struct SendMessageRequest {
message : Message
configuration : SendMessageConfig?
metadata : Json?
tenant : String?
}
///|
/// A request to send `message` with nothing else set.
pub fn SendMessageRequest::new(message : Message) -> SendMessageRequest {
{ message, configuration: None, metadata: None, tenant: None, }
}
///|
pub impl @json.ToJson for SendMessageConfig with fn to_json(self) {
let fields = object()
if self.return_immediately {
fields["returnImmediately"] = Json::boolean(true)
}
put_int(fields, "historyLength", self.history_length)
if self.accepted_output_modes.length() > 0 {
fields["acceptedOutputModes"] = strings(self.accepted_output_modes)
}
match self.push {
Some(push) => fields["taskPushNotificationConfig"] = push.to_json()
None => ()
}
Json::object(fields)
}
///|
pub impl @json.FromJson for SendMessageConfig with fn from_json(json, path) {
let fields = as_object(json, path)
{
return_immediately: pick_bool(
fields, "returnImmediately", "return_immediately",
).unwrap_or(false),
history_length: pick_int(fields, "historyLength", "history_length"),
accepted_output_modes: match
pick(fields, "acceptedOutputModes", "accepted_output_modes") {
Some(Array(items)) => {
let modes : Array[String] = []
for item in items {
if item is String(mode) {
modes.push(mode)
}
}
modes
}
_ => {
let none : Array[String] = []
none
}
},
push: match
pick_object(
fields, "taskPushNotificationConfig", "task_push_notification_config",
) {
Some(config) =>
Some(
TaskPushConfig::from_json(
Json::object(config),
path.add_key("taskPushNotificationConfig"),
),
)
None => None
},
}
}
///|
pub impl @json.ToJson for SendMessageRequest with fn to_json(self) {
let fields = object()
fields["message"] = self.message.to_json()
match self.configuration {
Some(configuration) => fields["configuration"] = configuration.to_json()
None => ()
}
put_json(fields, "metadata", self.metadata)
put_str(fields, "tenant", self.tenant)
Json::object(fields)
}
///|
pub impl @json.FromJson for SendMessageRequest with fn from_json(json, path) {
let fields = as_object(json, path)
{
message: match fields.get("message") {
Some(message) => Message::from_json(message, path.add_key("message"))
None => raise wrong_type(path, "message", "a message object")
},
configuration: read_opt(
fields,
"configuration",
path,
SendMessageConfig::from_json,
),
metadata: fields.get("metadata"),
tenant: opt_str(fields, "tenant"),
}
}
///|
/// The filters a list request carries (A2A `ListTasksRequest`).
#derive(Eq)
pub(all) struct ListTasksQuery {
context_id : String?
state : TaskState?
page_size : Int32?
page_token : String?
history_length : Int32?
status_timestamp_after : String?
include_artifacts : Bool
tenant : String?
}
///|
/// No filter at all: every task the caller may see.
pub fn ListTasksQuery::all() -> ListTasksQuery {
{
context_id: None,
state: None,
page_size: None,
page_token: None,
history_length: None,
status_timestamp_after: None,
include_artifacts: false,
tenant: None,
}
}
///|
/// The same query, limited to one conversation.
pub fn ListTasksQuery::in_context(
self : ListTasksQuery,
context_id : String,
) -> ListTasksQuery {
{ ..self, context_id: Some(context_id), }
}
///|
/// One page of tasks (A2A `ListTasksResponse`).
#derive(Eq)
pub(all) struct TaskPage {
tasks : Array[Task]
next_page_token : String
page_size : Int32
total_size : Int32
}
///|
pub impl @json.ToJson for ListTasksQuery with fn to_json(self) {
let fields = object()
put_str(fields, "contextId", self.context_id)
match self.state {
Some(state) => fields["status"] = state.to_json()
None => ()
}
put_int(fields, "pageSize", self.page_size)
put_str(fields, "pageToken", self.page_token)
put_int(fields, "historyLength", self.history_length)
put_str(fields, "statusTimestampAfter", self.status_timestamp_after)
if self.include_artifacts {
fields["includeArtifacts"] = Json::boolean(true)
}
put_str(fields, "tenant", self.tenant)
Json::object(fields)
}
///|
pub impl @json.FromJson for ListTasksQuery with fn from_json(json, path) {
let fields = as_object(json, path)
{
context_id: pick_str(fields, "contextId", "context_id"),
state: match pick_str(fields, "status", "status") {
Some(name) => Some(TaskState::from_wire(name))
None => None
},
page_size: pick_int(fields, "pageSize", "page_size"),
page_token: pick_str(fields, "pageToken", "page_token"),
history_length: pick_int(fields, "historyLength", "history_length"),
status_timestamp_after: pick_str(
fields, "statusTimestampAfter", "status_timestamp_after",
),
include_artifacts: pick_bool(
fields, "includeArtifacts", "include_artifacts",
).unwrap_or(false),
tenant: opt_str(fields, "tenant"),
}
}
///|
pub impl @json.ToJson for TaskPage with fn to_json(self) {
Json::object({
"tasks": Json::array(self.tasks.map(Task::to_json)),
"nextPageToken": Json::string(self.next_page_token),
"pageSize": Json::number(
self.page_size.to_double(),
repr=self.page_size.to_string(),
),
"totalSize": Json::number(
self.total_size.to_double(),
repr=self.total_size.to_string(),
),
})
}
///|
pub impl @json.FromJson for TaskPage with fn from_json(json, path) {
let fields = as_object(json, path)
{
tasks: read_array(fields, "tasks", path, Task::from_json),
next_page_token: opt_str(fields, "nextPageToken").unwrap_or(""),
page_size: opt_int(fields, "pageSize").unwrap_or(0),
total_size: opt_int(fields, "totalSize").unwrap_or(0),
}
}
///|
pub extend SendMessageConfig with @json.ToJson::{to_json}
///|
pub extend SendMessageRequest with @json.ToJson::{to_json}
///|
pub extend ListTasksQuery with @json.ToJson::{to_json}
///|
pub extend TaskPage with @json.ToJson::{to_json}
///|
pub extend SendMessageConfig with @json.FromJson::{from_json}
///|
pub extend SendMessageRequest with @json.FromJson::{from_json}
///|
pub extend ListTasksQuery with @json.FromJson::{from_json}
///|
pub extend TaskPage with @json.FromJson::{from_json}
///|
pub extend Method with Eq::{not_equal, equal}
///|
pub extend SendMessageConfig with Eq::{not_equal, equal}
///|
pub extend SendMessageRequest with Eq::{not_equal, equal}
///|
pub extend ListTasksQuery with Eq::{not_equal, equal}
///|
pub extend TaskPage with Eq::{not_equal, equal}