modular_agent_core/
spec.rs1use std::ops::Not;
2
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5
6use crate::FnvIndexMap;
7use crate::config::AgentConfigs;
8use crate::definition::AgentConfigSpecs;
9use crate::error::AgentError;
10
11pub type PresetSpecs = FnvIndexMap<String, PresetSpec>;
13
14#[derive(Clone, Debug, Default, Deserialize, Serialize)]
20pub struct PresetSpec {
21 pub agents: Vec<AgentSpec>,
23
24 pub connections: Vec<ConnectionSpec>,
26
27 #[serde(flatten)]
31 pub extensions: FnvIndexMap<String, Value>,
32}
33
34impl PresetSpec {
35 pub fn add_agent(&mut self, agent: AgentSpec) {
37 self.agents.push(agent);
38 }
39
40 pub fn remove_agent(&mut self, agent_id: &str) {
42 self.agents.retain(|agent| agent.id != agent_id);
43 }
44
45 pub fn add_connection(&mut self, connection: ConnectionSpec) {
47 self.connections.push(connection);
48 }
49
50 pub fn remove_connection(&mut self, connection: &ConnectionSpec) -> Option<ConnectionSpec> {
55 let index = self.connections.iter().position(|c| c == connection)?;
56 Some(self.connections.remove(index))
57 }
58
59 pub fn to_json(&self) -> Result<String, AgentError> {
61 let json = serde_json::to_string_pretty(self)
62 .map_err(|e| AgentError::SerializationError(e.to_string()))?;
63 Ok(json)
64 }
65
66 pub fn from_json(json_str: &str) -> Result<Self, AgentError> {
68 let preset: PresetSpec = serde_json::from_str(json_str)
69 .map_err(|e| AgentError::SerializationError(e.to_string()))?;
70 Ok(preset)
71 }
72}
73
74#[derive(Debug, Clone, Default, Serialize, Deserialize)]
79pub struct AgentSpec {
80 #[serde(skip_serializing_if = "String::is_empty", default)]
82 pub id: String,
83
84 #[serde(skip_serializing_if = "String::is_empty", default)]
86 pub def_name: String,
87
88 #[serde(skip_serializing_if = "Option::is_none", default)]
90 pub inputs: Option<Vec<String>>,
91
92 #[serde(skip_serializing_if = "Option::is_none", default)]
94 pub outputs: Option<Vec<String>>,
95
96 #[serde(skip_serializing_if = "Option::is_none")]
98 pub configs: Option<AgentConfigs>,
99
100 #[serde(skip_serializing_if = "Option::is_none")]
102 pub config_specs: Option<AgentConfigSpecs>,
103
104 #[serde(default, skip_serializing_if = "<&bool>::not")]
106 pub disabled: bool,
107
108 #[serde(flatten)]
110 pub extensions: FnvIndexMap<String, serde_json::Value>,
111}
112
113impl AgentSpec {
114 pub fn update(&mut self, value: &Value) -> Result<(), AgentError> {
119 let update_map = value
120 .as_object()
121 .ok_or_else(|| AgentError::SerializationError("Expected JSON object".to_string()))?;
122
123 for (k, v) in update_map {
124 match k.as_str() {
125 "id" => {
126 if let Some(id_str) = v.as_str() {
127 self.id = id_str.to_string();
128 }
129 }
130 "def_name" => {
131 if let Some(def_name_str) = v.as_str() {
132 self.def_name = def_name_str.to_string();
133 }
134 }
135 "inputs" => {
136 if let Some(inputs_array) = v.as_array() {
137 self.inputs = Some(
138 inputs_array
139 .iter()
140 .filter_map(|v| v.as_str().map(|s| s.to_string()))
141 .collect(),
142 );
143 }
144 }
145 "outputs" => {
146 if let Some(outputs_array) = v.as_array() {
147 self.outputs = Some(
148 outputs_array
149 .iter()
150 .filter_map(|v| v.as_str().map(|s| s.to_string()))
151 .collect(),
152 );
153 }
154 }
155 "configs" => {
156 let configs: AgentConfigs = serde_json::from_value(v.clone())
157 .map_err(|e| AgentError::SerializationError(e.to_string()))?;
158 self.configs = Some(configs);
159 }
160 "disabled" => {
161 if let Some(disabled_bool) = v.as_bool() {
162 self.disabled = disabled_bool;
163 }
164 }
165 _ => {
166 if v.is_null() {
168 self.extensions.shift_remove(k);
169 } else {
170 self.extensions.insert(k.clone(), v.clone());
171 }
172 }
173 }
174 }
175
176 Ok(())
177 }
178}
179
180#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
185pub struct ConnectionSpec {
186 pub source: String,
188
189 pub source_handle: String,
191
192 pub target: String,
194
195 pub target_handle: String,
197}