1use std::collections::HashMap;
2use std::ops::Not;
3
4use serde::{Deserialize, Serialize};
5
6use super::agent::Agent;
7use super::askit::ASKit;
8use super::config::AgentConfig;
9use super::data::AgentValue;
10use super::error::AgentError;
11
12pub type AgentDefinitions = HashMap<String, AgentDefinition>;
13
14#[derive(Debug, Default, Serialize, Deserialize, Clone)]
15pub struct AgentDefinition {
16 pub kind: String,
17
18 pub name: String,
19
20 #[serde(skip_serializing_if = "Option::is_none")]
21 pub title: Option<String>,
22
23 #[serde(skip_serializing_if = "Option::is_none")]
24 pub description: Option<String>,
25
26 #[serde(skip_serializing_if = "Option::is_none")]
27 pub category: Option<String>,
28
29 #[serde(skip_serializing_if = "Option::is_none")]
30 pub inputs: Option<Vec<String>>,
31
32 #[serde(skip_serializing_if = "Option::is_none")]
33 pub outputs: Option<Vec<String>>,
34
35 #[serde(skip_serializing_if = "Option::is_none")]
36 pub default_config: Option<AgentDefaultConfig>,
37
38 #[serde(skip_serializing_if = "Option::is_none")]
39 pub global_config: Option<AgentGlobalConfig>,
40
41 #[serde(skip_serializing_if = "Option::is_none")]
42 pub display_config: Option<AgentDisplayConfig>,
43
44 #[serde(default, skip_serializing_if = "<&bool>::not")]
45 pub native_thread: bool,
46
47 #[serde(skip)]
48 pub new_boxed: Option<AgentNewBoxedFn>,
49}
50
51pub type AgentDefaultConfig = Vec<(String, AgentConfigEntry)>;
52pub type AgentGlobalConfig = Vec<(String, AgentConfigEntry)>;
53
54#[derive(Debug, Default, Serialize, Deserialize, Clone)]
55pub struct AgentConfigEntry {
56 pub value: AgentValue,
57
58 #[serde(rename = "type")]
59 pub type_: Option<String>,
60
61 #[serde(skip_serializing_if = "Option::is_none")]
62 pub title: Option<String>,
63
64 #[serde(skip_serializing_if = "Option::is_none")]
65 pub description: Option<String>,
66
67 #[serde(default, skip_serializing_if = "<&bool>::not")]
70 pub hidden: bool,
71}
72
73pub type AgentDisplayConfig = Vec<(String, AgentDisplayConfigEntry)>;
74
75#[derive(Debug, Default, Serialize, Deserialize, Clone)]
76pub struct AgentDisplayConfigEntry {
77 #[serde(rename = "type")]
78 pub type_: Option<String>,
79
80 #[serde(skip_serializing_if = "Option::is_none")]
81 pub title: Option<String>,
82
83 #[serde(skip_serializing_if = "Option::is_none")]
84 pub description: Option<String>,
85
86 #[serde(default, skip_serializing_if = "<&bool>::not")]
87 pub hide_title: bool,
88}
89
90pub type AgentNewBoxedFn = fn(
99 askit: ASKit,
100 id: String,
101 def_name: String,
102 config: Option<AgentConfig>,
103) -> Result<Box<dyn Agent + Send + Sync>, AgentError>;
104
105impl AgentDefinition {
106 pub fn new(
107 kind: impl Into<String>,
108 name: impl Into<String>,
109 new_boxed: Option<AgentNewBoxedFn>,
110 ) -> Self {
111 Self {
112 kind: kind.into(),
113 name: name.into(),
114 new_boxed,
115 ..Default::default()
116 }
117 }
118
119 pub fn with_title(mut self, title: &str) -> Self {
120 self.title = Some(title.into());
121 self
122 }
123
124 pub fn with_description(mut self, description: &str) -> Self {
125 self.description = Some(description.into());
126 self
127 }
128
129 pub fn with_category(mut self, category: &str) -> Self {
130 self.category = Some(category.into());
131 self
132 }
133
134 pub fn with_inputs(mut self, inputs: Vec<&str>) -> Self {
135 self.inputs = Some(inputs.into_iter().map(|x| x.into()).collect());
136 self
137 }
138
139 pub fn with_outputs(mut self, outputs: Vec<&str>) -> Self {
140 self.outputs = Some(outputs.into_iter().map(|x| x.into()).collect());
141 self
142 }
143
144 pub fn with_default_config(mut self, config: AgentDefaultConfig) -> Self {
145 self.default_config = Some(config);
146 self
147 }
148
149 #[allow(unused)]
150 pub fn with_global_config(mut self, config: AgentGlobalConfig) -> Self {
151 self.global_config = Some(config);
152 self
153 }
154
155 pub fn with_display_config(mut self, config: AgentDisplayConfig) -> Self {
156 self.display_config = Some(config);
157 self
158 }
159
160 pub fn use_native_thread(mut self) -> Self {
161 self.native_thread = true;
162 self
163 }
164}
165
166impl AgentConfigEntry {
167 pub fn new(value: AgentValue, type_: &str) -> Self {
168 Self {
169 value,
170 type_: Some(type_.into()),
171 ..Default::default()
172 }
173 }
174
175 pub fn with_title(mut self, title: &str) -> Self {
176 self.title = Some(title.into());
177 self
178 }
179
180 pub fn with_description(mut self, description: &str) -> Self {
181 self.description = Some(description.into());
182 self
183 }
184
185 pub fn with_hidden(mut self) -> Self {
186 self.hidden = true;
187 self
188 }
189}
190
191impl AgentDisplayConfigEntry {
192 pub fn new(type_: &str) -> Self {
193 Self {
194 type_: Some(type_.into()),
195 ..Default::default()
196 }
197 }
198
199 pub fn with_hide_title(mut self) -> Self {
200 self.hide_title = true;
201 self
202 }
203
204 #[allow(unused)]
205 pub fn with_title(mut self, title: &str) -> Self {
206 self.title = Some(title.into());
207 self
208 }
209
210 #[allow(unused)]
211 pub fn with_description(mut self, description: &str) -> Self {
212 self.description = Some(description.into());
213 self
214 }
215}
216
217#[cfg(test)]
218mod tests {
219 use super::*;
220
221 #[test]
222 fn test_agent_definition() {
223 let def = AgentDefinition::default();
224 assert_eq!(def.name, "");
225 }
226
227 #[test]
228 fn test_agent_definition_new_default() {
229 let def = AgentDefinition::new(
230 "test",
231 "echo",
232 Some(|_app, _id, _def_name, _config| {
233 Err(AgentError::NotImplemented("Echo agent".into()))
234 }),
235 );
236
237 assert_eq!(def.kind, "test");
238 assert_eq!(def.name, "echo");
239 assert!(def.title.is_none());
240 assert!(def.category.is_none());
241 assert!(def.inputs.is_none());
242 assert!(def.outputs.is_none());
243 assert!(def.display_config.is_none());
244 }
245
246 #[test]
247 fn test_agent_definition_new() {
248 let def = echo_agent_definition();
249
250 assert_eq!(def.kind, "test");
251 assert_eq!(def.name, "echo");
252 assert_eq!(def.title.unwrap(), "Echo");
253 assert_eq!(def.category.unwrap(), "Test");
254 assert_eq!(def.inputs.unwrap(), vec!["in"]);
255 assert_eq!(def.outputs.unwrap(), vec!["out"]);
256 let display_config = def.display_config.unwrap();
257 assert_eq!(display_config.len(), 2);
258 let entry = &display_config[0];
259 assert_eq!(entry.0, "value");
260 assert_eq!(entry.1.type_.as_ref().unwrap(), "string");
261 assert_eq!(entry.1.title.as_ref().unwrap(), "display_title");
262 assert_eq!(entry.1.description.as_ref().unwrap(), "display_description");
263 assert_eq!(entry.1.hide_title, false);
264 let entry = &display_config[1];
265 assert_eq!(entry.0, "hide_title_value");
266 assert_eq!(entry.1.type_.as_ref().unwrap(), "integer");
267 assert_eq!(entry.1.title, None);
268 assert_eq!(entry.1.description, None);
269 assert_eq!(entry.1.hide_title, true);
270 }
271
272 #[test]
273 fn test_serialize_agent_definition() {
274 let def = AgentDefinition::new(
275 "test",
276 "echo",
277 Some(|_app, _id, _def_name, _config| {
278 Err(AgentError::NotImplemented("Echo agent".into()))
279 }),
280 );
281 let json = serde_json::to_string(&def).unwrap();
282 assert_eq!(json, r#"{"kind":"test","name":"echo"}"#);
283 }
284
285 #[test]
286 fn test_serialize_echo_agent_definition() {
287 let def = echo_agent_definition();
288 let json = serde_json::to_string(&def).unwrap();
289 print!("{}", json);
290 assert_eq!(
291 json,
292 r#"{"kind":"test","name":"echo","title":"Echo","category":"Test","inputs":["in"],"outputs":["out"],"display_config":[["value",{"type":"string","title":"display_title","description":"display_description"}],["hide_title_value",{"type":"integer","hide_title":true}]]}"#
293 );
294 }
295
296 #[test]
297 fn test_deserialize_echo_agent_definition() {
298 let json = r#"{"kind":"test","name":"echo","title":"Echo","category":"Test","inputs":["in"],"outputs":["out"],"display_config":[["value",{"type":"string","title":"display_title","description":"display_description"}],["hide_title_value",{"type":"integer","hide_title":true}]]}"#;
299 let def: AgentDefinition = serde_json::from_str(json).unwrap();
300 assert_eq!(def.kind, "test");
301 assert_eq!(def.name, "echo");
302 assert_eq!(def.title.unwrap(), "Echo");
303 assert_eq!(def.category.unwrap(), "Test");
304 assert_eq!(def.inputs.unwrap(), vec!["in"]);
305 assert_eq!(def.outputs.unwrap(), vec!["out"]);
306 let display_config = def.display_config.unwrap();
307 assert_eq!(display_config.len(), 2);
308 let entry = &display_config[0];
309 assert_eq!(entry.0, "value");
310 assert_eq!(entry.1.type_.as_ref().unwrap(), "string");
311 assert_eq!(entry.1.title.as_ref().unwrap(), "display_title");
312 assert_eq!(entry.1.description.as_ref().unwrap(), "display_description");
313 assert_eq!(entry.1.hide_title, false);
314 let entry = &display_config[1];
315 assert_eq!(entry.0, "hide_title_value");
316 assert_eq!(entry.1.type_.as_ref().unwrap(), "integer");
317 assert_eq!(entry.1.title, None);
318 assert_eq!(entry.1.description, None);
319 assert_eq!(entry.1.hide_title, true);
320 }
321
322 fn echo_agent_definition() -> AgentDefinition {
323 AgentDefinition::new(
324 "test",
325 "echo",
326 Some(|_app, _id, _def_name, _config| {
327 Err(AgentError::NotImplemented("Echo agent".into()))
328 }),
329 )
330 .with_title("Echo")
331 .with_category("Test")
332 .with_inputs(vec!["in"])
333 .with_outputs(vec!["out"])
334 .with_display_config(vec![
335 (
336 "value".into(),
337 AgentDisplayConfigEntry::new("string")
338 .with_title("display_title")
339 .with_description("display_description"),
340 ),
341 (
342 "hide_title_value".into(),
343 AgentDisplayConfigEntry::new("integer").with_hide_title(),
344 ),
345 ])
346 }
347}