agent_stream_kit/
definition.rs

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::AgentConfigs;
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_configs: Option<AgentDefaultConfigs>,
37
38    #[serde(skip_serializing_if = "Option::is_none")]
39    pub global_configs: Option<AgentGlobalConfigs>,
40
41    #[serde(skip_serializing_if = "Option::is_none")]
42    pub display_configs: Option<AgentDisplayConfigs>,
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 AgentDefaultConfigs = Vec<(String, AgentConfigEntry)>;
52pub type AgentGlobalConfigs = 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    /// Indicates whether this configuration entry should be hidden from the user interface.
68    /// If set to `true`, the entry will be hidden. The default behavior is to show the entry.
69    #[serde(default, skip_serializing_if = "<&bool>::not")]
70    pub hidden: bool,
71}
72
73pub type AgentDisplayConfigs = 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
90// #[derive(Debug, Default, Serialize, Deserialize, Clone)]
91// pub struct CommandConfig {
92//     pub cmd: String,
93//     pub args: Option<Vec<String>>,
94
95//     pub dir: Option<String>,
96// }
97
98pub type AgentNewBoxedFn = fn(
99    askit: ASKit,
100    id: String,
101    def_name: String,
102    configs: Option<AgentConfigs>,
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_configs(mut self, configs: Vec<(&str, AgentConfigEntry)>) -> Self {
145        self.default_configs = Some(configs.into_iter().map(|(k, v)| (k.into(), v)).collect());
146        self
147    }
148
149    #[allow(unused)]
150    pub fn with_global_configs(mut self, configs: Vec<(&str, AgentConfigEntry)>) -> Self {
151        self.global_configs = Some(configs.into_iter().map(|(k, v)| (k.into(), v)).collect());
152        self
153    }
154
155    pub fn with_display_configs(mut self, configs: Vec<(&str, AgentDisplayConfigEntry)>) -> Self {
156        self.display_configs = Some(configs.into_iter().map(|(k, v)| (k.into(), v)).collect());
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<V: Into<AgentValue>>(value: V, type_: &str) -> Self {
168        Self {
169            value: value.into(),
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, _configs| {
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_configs.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_configs = def.display_configs.unwrap();
257        assert_eq!(display_configs.len(), 2);
258        let entry = &display_configs[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_configs[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, _configs| {
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_configs":[["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_configs":[["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_configs = def.display_configs.unwrap();
307        assert_eq!(display_configs.len(), 2);
308        let entry = &display_configs[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_configs[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, _configs| {
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_configs(vec![
335            (
336                "value",
337                AgentDisplayConfigEntry::new("string")
338                    .with_title("display_title")
339                    .with_description("display_description"),
340            ),
341            (
342                "hide_title_value",
343                AgentDisplayConfigEntry::new("integer").with_hide_title(),
344            ),
345        ])
346    }
347}