vex_api/a2a/
agent_card.rs1use serde::{Deserialize, Serialize};
13
14#[derive(Debug, Clone, Serialize, Deserialize)]
29pub struct AgentCard {
30 pub name: String,
32 pub description: String,
34 pub version: String,
36 pub skills: Vec<Skill>,
38 pub authentication: AuthConfig,
40 pub provider: ProviderInfo,
42 #[serde(skip_serializing_if = "Option::is_none")]
44 pub docs_url: Option<String>,
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
49pub struct Skill {
50 pub id: String,
52 pub name: String,
54 pub description: String,
56 #[serde(skip_serializing_if = "Option::is_none")]
58 pub input_schema: Option<serde_json::Value>,
59 #[serde(skip_serializing_if = "Option::is_none")]
61 pub output_schema: Option<serde_json::Value>,
62}
63
64#[derive(Debug, Clone, Serialize, Deserialize)]
66pub struct AuthConfig {
67 pub schemes: Vec<String>,
69 #[serde(skip_serializing_if = "Option::is_none")]
71 pub token_endpoint: Option<String>,
72 #[serde(skip_serializing_if = "Option::is_none")]
74 pub oidc_discovery: Option<String>,
75}
76
77#[derive(Debug, Clone, Serialize, Deserialize)]
79pub struct ProviderInfo {
80 pub organization: String,
82 #[serde(skip_serializing_if = "Option::is_none")]
84 pub contact: Option<String>,
85}
86
87impl AgentCard {
88 pub fn new(name: impl Into<String>) -> Self {
90 Self {
91 name: name.into(),
92 description: String::new(),
93 version: "1.0".to_string(),
94 skills: Vec::new(),
95 authentication: AuthConfig::default(),
96 provider: ProviderInfo {
97 organization: "VEX".to_string(),
98 contact: None,
99 },
100 docs_url: None,
101 }
102 }
103
104 pub fn with_description(mut self, desc: impl Into<String>) -> Self {
106 self.description = desc.into();
107 self
108 }
109
110 pub fn with_skill(mut self, id: impl Into<String>, description: impl Into<String>) -> Self {
112 let id_str = id.into();
113 self.skills.push(Skill {
114 id: id_str.clone(),
115 name: id_str,
116 description: description.into(),
117 input_schema: None,
118 output_schema: None,
119 });
120 self
121 }
122
123 pub fn with_skill_full(mut self, skill: Skill) -> Self {
125 self.skills.push(skill);
126 self
127 }
128
129 pub fn with_docs(mut self, url: impl Into<String>) -> Self {
131 self.docs_url = Some(url.into());
132 self
133 }
134
135 pub fn with_auth(mut self, auth: AuthConfig) -> Self {
137 self.authentication = auth;
138 self
139 }
140
141 pub fn vex_default() -> Self {
143 Self::new("vex-agent")
144 .with_description(
145 "VEX Protocol agent with adversarial verification and cryptographic proofs",
146 )
147 .with_skill("verify", "Verify a claim using adversarial red/blue debate")
148 .with_skill("hash", "Compute SHA-256 hash of content")
149 .with_skill("merkle_root", "Get current Merkle root for audit chain")
150 .with_docs("https://provnai.dev/docs")
151 .with_auth(AuthConfig {
152 schemes: vec!["bearer".to_string(), "api_key".to_string()],
153 token_endpoint: None,
154 oidc_discovery: None,
155 })
156 }
157}
158
159impl Default for AuthConfig {
160 fn default() -> Self {
161 Self {
162 schemes: vec!["bearer".to_string()],
163 token_endpoint: None,
164 oidc_discovery: None,
165 }
166 }
167}
168
169impl Skill {
170 pub fn new(
172 id: impl Into<String>,
173 name: impl Into<String>,
174 description: impl Into<String>,
175 ) -> Self {
176 Self {
177 id: id.into(),
178 name: name.into(),
179 description: description.into(),
180 input_schema: None,
181 output_schema: None,
182 }
183 }
184
185 pub fn with_input_schema(mut self, schema: serde_json::Value) -> Self {
187 self.input_schema = Some(schema);
188 self
189 }
190
191 pub fn with_output_schema(mut self, schema: serde_json::Value) -> Self {
193 self.output_schema = Some(schema);
194 self
195 }
196}
197
198#[cfg(test)]
199mod tests {
200 use super::*;
201
202 #[test]
203 fn test_agent_card_new() {
204 let card = AgentCard::new("test-agent");
205 assert_eq!(card.name, "test-agent");
206 assert_eq!(card.version, "1.0");
207 assert!(card.skills.is_empty());
208 }
209
210 #[test]
211 fn test_agent_card_builder() {
212 let card = AgentCard::new("vex")
213 .with_description("VEX verifier")
214 .with_skill("verify", "Verify claims");
215
216 assert_eq!(card.description, "VEX verifier");
217 assert_eq!(card.skills.len(), 1);
218 assert_eq!(card.skills[0].id, "verify");
219 }
220
221 #[test]
222 fn test_vex_default() {
223 let card = AgentCard::vex_default();
224 assert_eq!(card.name, "vex-agent");
225 assert!(card.skills.len() >= 3);
226 assert!(card.docs_url.is_some());
227 }
228
229 #[test]
230 fn test_serialization() {
231 let card = AgentCard::vex_default();
232 let json = serde_json::to_string_pretty(&card).unwrap();
233 assert!(json.contains("vex-agent"));
234 assert!(json.contains("verify"));
235 }
236}