use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use super::Metadata;
use super::security::{SecurityRequirement, SecurityScheme};
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
#[serde(transparent)]
pub struct TransportProtocol(pub String);
impl TransportProtocol {
pub const JSONRPC: &'static str = "JSONRPC";
pub const GRPC: &'static str = "GRPC";
pub const HTTP_JSON: &'static str = "HTTP+JSON";
pub fn new(protocol: impl Into<String>) -> Self {
Self(protocol.into())
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
#[must_use]
pub fn is_jsonrpc(&self) -> bool {
self.0 == Self::JSONRPC
}
#[must_use]
pub fn is_grpc(&self) -> bool {
self.0 == Self::GRPC
}
#[must_use]
pub fn is_http_json(&self) -> bool {
self.0 == Self::HTTP_JSON
}
}
impl std::fmt::Display for TransportProtocol {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
}
}
impl From<&str> for TransportProtocol {
fn from(s: &str) -> Self {
Self(s.to_owned())
}
}
impl From<String> for TransportProtocol {
fn from(s: String) -> Self {
Self(s)
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct AgentInterface {
pub url: String,
pub protocol_binding: TransportProtocol,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tenant: Option<String>,
pub protocol_version: String,
}
impl AgentInterface {
pub fn new(url: impl Into<String>, protocol_binding: TransportProtocol) -> Self {
Self {
url: url.into(),
protocol_binding,
tenant: None,
protocol_version: crate::PROTOCOL_VERSION.to_owned(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct AgentCard {
pub name: String,
pub description: String,
pub supported_interfaces: Vec<AgentInterface>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub provider: Option<AgentProvider>,
pub version: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub documentation_url: Option<String>,
pub capabilities: AgentCapabilities,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub security_schemes: HashMap<String, SecurityScheme>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub security_requirements: Vec<SecurityRequirement>,
pub default_input_modes: Vec<String>,
pub default_output_modes: Vec<String>,
pub skills: Vec<AgentSkill>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub signatures: Vec<AgentCardSignature>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub icon_url: Option<String>,
}
impl AgentCard {
pub fn new(
name: impl Into<String>,
description: impl Into<String>,
interfaces: Vec<AgentInterface>,
) -> Self {
Self {
name: name.into(),
description: description.into(),
supported_interfaces: interfaces,
provider: None,
version: "1.0.0".into(),
documentation_url: None,
capabilities: AgentCapabilities::default(),
security_schemes: HashMap::new(),
security_requirements: Vec::new(),
default_input_modes: vec!["text/plain".into()],
default_output_modes: vec!["text/plain".into()],
skills: Vec::new(),
signatures: Vec::new(),
icon_url: None,
}
}
#[must_use]
pub fn supports_streaming(&self) -> bool {
self.capabilities.streaming.unwrap_or(false)
}
#[must_use]
pub fn supports_push_notifications(&self) -> bool {
self.capabilities.push_notifications.unwrap_or(false)
}
#[must_use]
pub fn supports_extended_card(&self) -> bool {
self.capabilities.extended_agent_card.unwrap_or(false)
}
#[must_use]
pub fn find_skill(&self, skill_id: &str) -> Option<&AgentSkill> {
self.skills.iter().find(|s| s.id == skill_id)
}
#[must_use]
pub fn preferred_interface(&self) -> Option<&AgentInterface> {
self.supported_interfaces.first()
}
#[must_use]
pub fn find_interface(&self, protocol: &TransportProtocol) -> Option<&AgentInterface> {
self.supported_interfaces
.iter()
.find(|i| &i.protocol_binding == protocol)
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct AgentCapabilities {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub streaming: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub push_notifications: Option<bool>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub extensions: Vec<AgentExtension>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub extended_agent_card: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct AgentSkill {
pub id: String,
pub name: String,
pub description: String,
pub tags: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub examples: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub input_modes: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub output_modes: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub security_requirements: Vec<SecurityRequirement>,
}
impl AgentSkill {
pub fn new(
id: impl Into<String>,
name: impl Into<String>,
description: impl Into<String>,
tags: Vec<String>,
) -> Self {
Self {
id: id.into(),
name: name.into(),
description: description.into(),
tags,
examples: Vec::new(),
input_modes: Vec::new(),
output_modes: Vec::new(),
security_requirements: Vec::new(),
}
}
#[must_use]
pub fn with_examples(mut self, examples: Vec<String>) -> Self {
self.examples = examples;
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct AgentExtension {
pub uri: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(default, skip_serializing_if = "super::is_false")]
pub required: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub params: Option<Metadata>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct AgentProvider {
pub organization: String,
pub url: String,
}
impl AgentProvider {
pub fn new(organization: impl Into<String>, url: impl Into<String>) -> Self {
Self {
organization: organization.into(),
url: url.into(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct AgentCardSignature {
pub protected: String,
pub signature: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub header: Option<Metadata>,
}