use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
pub const A2A_VERSION: &str = "0.10.0";
#[derive(Serialize, Deserialize, Debug, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct AgentCard {
pub version: String,
pub name: String,
pub description: String,
pub url: String,
#[serde(default)]
pub icon_url: Option<String>,
#[serde(default)]
pub documentation_url: Option<String>,
#[serde(default)]
pub provider: Option<AgentProvider>,
#[serde(default)]
pub preferred_transport: Option<String>,
pub capabilities: AgentCapabilities,
pub default_input_modes: Vec<String>,
pub default_output_modes: Vec<String>,
pub skills: Vec<AgentSkill>,
#[serde(default)]
pub security_schemes: HashMap<String, SecurityScheme>,
#[serde(default)]
pub security: Vec<HashMap<String, Vec<String>>>,
}
#[derive(Serialize, Deserialize, Debug, JsonSchema, Clone)]
#[serde(rename_all = "camelCase")]
pub struct AgentProvider {
pub organization: String,
pub url: String,
}
#[derive(Serialize, Deserialize, Debug, JsonSchema, Clone, Default)]
#[serde(rename_all = "camelCase")]
pub struct AgentCapabilities {
#[serde(default = "default_true")]
pub streaming: bool,
#[serde(default)]
pub push_notifications: bool,
#[serde(default = "default_true")]
pub state_transition_history: bool,
#[serde(default)]
pub extensions: Vec<AgentExtension>,
}
fn default_true() -> bool {
true
}
#[derive(Serialize, Deserialize, Debug, JsonSchema, Clone)]
#[serde(rename_all = "camelCase")]
pub struct AgentExtension {
pub uri: String,
#[serde(default)]
pub description: Option<String>,
#[serde(default)]
pub required: bool,
#[serde(default)]
pub params: Option<serde_json::Value>,
}
#[derive(Serialize, Deserialize, Debug, JsonSchema, Clone)]
#[serde(rename_all = "camelCase")]
pub struct AgentSkill {
pub id: String,
pub name: String,
pub description: String,
#[serde(default)]
pub tags: Vec<String>,
#[serde(default)]
pub examples: Vec<String>,
#[serde(default)]
pub input_modes: Option<Vec<String>>,
#[serde(default)]
pub output_modes: Option<Vec<String>>,
}
#[derive(Serialize, Deserialize, Debug, JsonSchema, Clone)]
#[serde(tag = "type", rename_all = "camelCase")]
pub enum SecurityScheme {
ApiKey(APIKeySecurityScheme),
Http(HTTPAuthSecurityScheme),
Oauth2(Box<OAuth2SecurityScheme>),
OpenIdConnect(OpenIdConnectSecurityScheme),
}
#[derive(Serialize, Deserialize, Debug, JsonSchema, Clone)]
#[serde(rename_all = "camelCase")]
pub struct APIKeySecurityScheme {
pub name: String,
#[serde(rename = "in")]
pub location: String,
#[serde(default)]
pub description: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, JsonSchema, Clone)]
#[serde(rename_all = "camelCase")]
pub struct HTTPAuthSecurityScheme {
pub scheme: String,
#[serde(default)]
pub bearer_format: Option<String>,
#[serde(default)]
pub description: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, JsonSchema, Clone)]
#[serde(rename_all = "camelCase")]
pub struct OAuth2SecurityScheme {
pub flows: OAuthFlows,
#[serde(default)]
pub description: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, JsonSchema, Clone)]
#[serde(rename_all = "camelCase")]
pub struct OpenIdConnectSecurityScheme {
pub open_id_connect_url: String,
#[serde(default)]
pub description: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, JsonSchema, Clone, Default)]
#[serde(rename_all = "camelCase")]
pub struct OAuthFlows {
#[serde(default)]
pub implicit: Option<ImplicitOAuthFlow>,
#[serde(default)]
pub password: Option<PasswordOAuthFlow>,
#[serde(default)]
pub client_credentials: Option<ClientCredentialsOAuthFlow>,
#[serde(default)]
pub authorization_code: Option<AuthorizationCodeOAuthFlow>,
}
#[derive(Serialize, Deserialize, Debug, JsonSchema, Clone)]
#[serde(rename_all = "camelCase")]
pub struct ImplicitOAuthFlow {
pub authorization_url: String,
#[serde(default)]
pub refresh_url: Option<String>,
pub scopes: HashMap<String, String>,
}
#[derive(Serialize, Deserialize, Debug, JsonSchema, Clone)]
#[serde(rename_all = "camelCase")]
pub struct PasswordOAuthFlow {
pub token_url: String,
#[serde(default)]
pub refresh_url: Option<String>,
pub scopes: HashMap<String, String>,
}
#[derive(Serialize, Deserialize, Debug, JsonSchema, Clone)]
#[serde(rename_all = "camelCase")]
pub struct ClientCredentialsOAuthFlow {
pub token_url: String,
#[serde(default)]
pub refresh_url: Option<String>,
pub scopes: HashMap<String, String>,
}
#[derive(Serialize, Deserialize, Debug, JsonSchema, Clone)]
#[serde(rename_all = "camelCase")]
pub struct AuthorizationCodeOAuthFlow {
pub authorization_url: String,
pub token_url: String,
#[serde(default)]
pub refresh_url: Option<String>,
pub scopes: HashMap<String, String>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct JsonRpcRequest {
pub jsonrpc: String,
pub method: String,
pub params: serde_json::Value,
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<serde_json::Value>,
}
#[derive(Serialize, Debug)]
pub struct JsonRpcResponse {
pub jsonrpc: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub result: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<JsonRpcError>,
pub id: Option<serde_json::Value>,
}
#[derive(Serialize, Debug)]
pub struct JsonRpcError {
pub code: i32,
pub message: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub data: Option<serde_json::Value>,
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct MessageSendParams {
pub message: Message,
#[serde(default)]
pub configuration: Option<MessageSendConfiguration>,
#[serde(default)]
pub metadata: Option<serde_json::Value>,
}
#[derive(Serialize, Deserialize, Debug, Default)]
#[serde(rename_all = "camelCase")]
pub struct MessageSendConfiguration {
pub accepted_output_modes: Vec<String>,
#[serde(default)]
pub blocking: bool,
#[serde(default)]
pub history_length: Option<u32>,
#[serde(default)]
pub push_notification_config: Option<PushNotificationConfig>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(untagged)]
pub enum MessageKind {
Message(Message),
TaskStatusUpdate(TaskStatusUpdateEvent),
Artifact(Artifact),
}
impl MessageKind {
pub fn set_update_props(&mut self, metadata: serde_json::Value, context_id: String) {
match self {
MessageKind::Message(ref mut m) => {
m.metadata = Some(metadata);
m.context_id = Some(context_id);
}
MessageKind::TaskStatusUpdate(ref mut m) => {
m.metadata = Some(metadata);
m.context_id = context_id;
}
MessageKind::Artifact(_) => {}
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct Message {
pub kind: EventKind,
pub message_id: String,
pub role: Role,
pub parts: Vec<Part>,
#[serde(default)]
pub context_id: Option<String>,
#[serde(default)]
pub task_id: Option<String>,
#[serde(default)]
pub reference_task_ids: Vec<String>,
#[serde(default)]
pub extensions: Vec<String>,
#[serde(default)]
pub metadata: Option<serde_json::Value>,
}
impl Default for Message {
fn default() -> Self {
Self {
message_id: Default::default(),
kind: EventKind::Message,
role: Role::Agent,
parts: vec![],
context_id: None,
task_id: None,
reference_task_ids: vec![],
extensions: vec![],
metadata: None,
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
pub enum EventKind {
#[default]
#[serde(rename = "message")]
Message,
#[serde(rename = "task")]
Task,
#[serde(rename = "status-update")]
TaskStatusUpdate,
#[serde(rename = "artifact-update")]
TaskArtifactUpdate,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn serialize_message_kind() {
let message_kind = MessageKind::Message(Message::default());
let serialized = serde_json::to_string(&message_kind).unwrap();
println!("{}", serialized);
let deserialized: MessageKind = serde_json::from_str(&serialized).unwrap();
match deserialized {
MessageKind::Message(msg) => {
assert_eq!(msg.kind, EventKind::Message);
assert_eq!(msg.role, Role::Agent);
}
_ => panic!("Expected MessageKind::Message"),
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
#[serde(rename_all = "camelCase")]
pub enum Role {
User,
#[default]
Agent,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(tag = "kind", rename_all = "camelCase")]
pub enum Part {
#[serde(rename = "text")]
Text(TextPart),
#[serde(rename = "file")]
File(FilePart),
#[serde(rename = "data")]
Data(DataPart),
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct TextPart {
pub text: String,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct FilePart {
pub file: FileObject,
#[serde(default)]
pub metadata: Option<serde_json::Value>,
}
impl FilePart {
pub fn mime_type(&self) -> Option<&str> {
match &self.file {
FileObject::WithUri { mime_type, .. } => mime_type.as_deref(),
FileObject::WithBytes { mime_type, .. } => mime_type.as_deref(),
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(untagged, rename_all = "camelCase")]
pub enum FileObject {
WithUri {
uri: String,
#[serde(default, rename = "mimeType")]
mime_type: Option<String>,
#[serde(default)]
name: Option<String>,
},
WithBytes {
bytes: String,
#[serde(default, rename = "mimeType")]
mime_type: Option<String>,
#[serde(default)]
name: Option<String>,
},
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct DataPart {
pub data: serde_json::Value,
}
#[derive(Serialize, Deserialize, Debug, Default)]
#[serde(rename_all = "camelCase")]
pub struct PushNotificationConfig {
pub url: String,
#[serde(default)]
pub token: Option<String>,
#[serde(default)]
pub id: Option<String>,
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct TaskIdParams {
pub id: String,
}
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
#[serde(rename_all = "camelCase")]
pub struct Task {
pub kind: EventKind,
pub id: String,
pub context_id: String,
pub status: TaskStatus,
#[serde(default)]
pub artifacts: Vec<Artifact>,
#[serde(default)]
pub history: Vec<Message>,
#[serde(default)]
pub metadata: Option<serde_json::Value>,
}
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
#[serde(rename_all = "camelCase")]
pub struct TaskStatus {
pub state: TaskState,
#[serde(default)]
pub message: Option<Message>,
#[serde(default)]
pub timestamp: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
#[serde(rename_all = "camelCase")]
pub enum TaskState {
#[default]
Submitted,
Working,
InputRequired,
Completed,
Canceled,
Failed,
Rejected,
AuthRequired,
Unknown,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct Artifact {
pub artifact_id: String,
pub parts: Vec<Part>,
#[serde(default)]
pub name: Option<String>,
#[serde(default)]
pub description: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct TaskStatusUpdateEvent {
pub kind: EventKind,
pub task_id: String,
pub context_id: String,
pub status: TaskStatus,
pub r#final: bool,
#[serde(default)]
pub metadata: Option<serde_json::Value>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct TaskArtifactUpdateEvent {
pub kind: EventKind,
pub task_id: String,
pub context_id: String,
pub artifact: Artifact,
#[serde(default)]
pub append: Option<bool>,
#[serde(default)]
pub last_chunk: Option<bool>,
#[serde(default)]
pub metadata: Option<serde_json::Value>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct TaskStatusBroadcastEvent {
pub r#type: String,
pub task_id: String,
pub thread_id: String,
pub agent_id: String,
pub status: String,
}