Skip to main content

modular_agent_core/
preset.rs

1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3
4use crate::error::AgentError;
5use crate::id::{new_id, update_ids};
6use crate::modular_agent::ModularAgent;
7use crate::spec::PresetSpec;
8use crate::{AgentSpec, ConnectionSpec};
9
10pub struct Preset {
11    id: String,
12
13    name: Option<String>,
14
15    running: bool,
16
17    spec: PresetSpec,
18}
19
20impl Preset {
21    /// Create a new preset with the given spec.
22    ///
23    /// The ids of the given spec, including agents and connections, are changed to new unique ids.
24    pub fn new(mut spec: PresetSpec) -> Self {
25        let (agents, connections) = update_ids(&spec.agents, &spec.connections);
26        spec.agents = agents;
27        spec.connections = connections;
28
29        Self {
30            id: new_id(),
31            name: None,
32            running: false,
33            spec,
34        }
35    }
36
37    pub fn id(&self) -> &str {
38        &self.id
39    }
40
41    pub fn spec(&self) -> &PresetSpec {
42        &self.spec
43    }
44
45    pub fn update_spec(&mut self, value: &Value) -> Result<(), AgentError> {
46        let update_map = value
47            .as_object()
48            .ok_or_else(|| AgentError::SerializationError("Expected JSON object".to_string()))?;
49
50        for (k, v) in update_map {
51            match k.as_str() {
52                "agents" => {
53                    // just ignore
54                }
55                "connections" => {
56                    // just ignore
57                }
58                _ => {
59                    // Update extensions
60                    self.spec.extensions.insert(k.clone(), v.clone());
61                }
62            }
63        }
64        Ok(())
65    }
66
67    pub fn running(&self) -> bool {
68        self.running
69    }
70
71    pub fn name(&self) -> Option<&str> {
72        self.name.as_deref()
73    }
74
75    pub fn set_name(&mut self, name: String) {
76        self.name = Some(name);
77    }
78
79    pub fn clear_name(&mut self) {
80        self.name = None;
81    }
82
83    pub fn add_agent(&mut self, agent: AgentSpec) {
84        self.spec.add_agent(agent);
85    }
86
87    pub fn remove_agent(&mut self, agent_id: &str) {
88        self.spec.remove_agent(agent_id);
89    }
90
91    pub fn add_connection(&mut self, connection: ConnectionSpec) {
92        self.spec.add_connection(connection);
93    }
94
95    pub fn remove_connection(&mut self, connection: &ConnectionSpec) -> Option<ConnectionSpec> {
96        self.spec.remove_connection(connection)
97    }
98
99    pub async fn start(&mut self, ma: &ModularAgent) -> Result<(), AgentError> {
100        if self.running {
101            // Already running
102            return Ok(());
103        }
104        self.running = true;
105
106        for agent in self.spec.agents.iter() {
107            if agent.disabled {
108                continue;
109            }
110            ma.start_agent(&agent.id).await.unwrap_or_else(|e| {
111                log::error!("Failed to start agent {}: {}", agent.id, e);
112            });
113        }
114
115        Ok(())
116    }
117
118    pub async fn stop(&mut self, ma: &ModularAgent) -> Result<(), AgentError> {
119        for agent in self.spec.agents.iter() {
120            ma.stop_agent(&agent.id).await.unwrap_or_else(|e| {
121                log::error!("Failed to stop agent {}: {}", agent.id, e);
122            });
123        }
124        self.running = false;
125        Ok(())
126    }
127}
128
129#[derive(Clone, Debug, Serialize, Deserialize)]
130pub struct PresetInfo {
131    pub id: String,
132    pub name: Option<String>,
133    pub running: bool,
134}
135
136impl From<&Preset> for PresetInfo {
137    fn from(preset: &Preset) -> Self {
138        Self {
139            id: preset.id.clone(),
140            name: preset.name.clone(),
141            running: preset.running,
142        }
143    }
144}