oxios_kernel/
agent_group.rs1use chrono::Utc;
11use oxios_ouroboros::Seed;
12use serde::{Deserialize, Serialize};
13use uuid::Uuid;
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
17pub enum OxiosAgentGroupStatus {
18 Pending,
20 Running,
22 Completed,
24 Failed,
26}
27
28#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct OxiosGroupAgent {
31 pub id: Uuid,
33 pub seed: Seed,
35 pub status: OxiosAgentGroupStatus,
37 pub result: Option<String>,
39}
40
41#[derive(Debug, Clone, Serialize, Deserialize)]
43pub struct OxiosAgentGroup {
44 pub id: Uuid,
46 pub parent_seed_id: Uuid,
48 pub agents: Vec<OxiosGroupAgent>,
50}
51
52impl OxiosAgentGroup {
53 pub fn new(parent_seed: &Seed, subtask_descriptions: Vec<String>) -> Self {
55 let agents = subtask_descriptions
56 .into_iter()
57 .map(|desc| {
58 let child_seed = Seed {
59 id: Uuid::new_v4(),
60 goal: desc,
61 constraints: parent_seed.constraints.clone(),
62 acceptance_criteria: vec!["Task completes successfully".into()],
63 ontology: parent_seed.ontology.clone(),
64 created_at: Utc::now(),
65 generation: parent_seed.generation + 1,
66 parent_seed_id: Some(parent_seed.id),
67 cspace_hint: parent_seed.cspace_hint.clone(),
68 original_request: parent_seed.original_request.clone(),
69 output_schema: None,
70 project_id: None,
71 };
72 OxiosGroupAgent {
73 id: child_seed.id,
74 seed: child_seed,
75 status: OxiosAgentGroupStatus::Pending,
76 result: None,
77 }
78 })
79 .collect();
80
81 Self {
82 id: Uuid::new_v4(),
83 parent_seed_id: parent_seed.id,
84 agents,
85 }
86 }
87
88 pub fn pending_agents(&self) -> Vec<&OxiosGroupAgent> {
90 self.agents
91 .iter()
92 .filter(|a| a.status == OxiosAgentGroupStatus::Pending)
93 .collect()
94 }
95
96 pub fn completed_agents(&self) -> Vec<&OxiosGroupAgent> {
98 self.agents
99 .iter()
100 .filter(|a| a.status == OxiosAgentGroupStatus::Completed)
101 .collect()
102 }
103
104 pub fn failed_agents(&self) -> Vec<&OxiosGroupAgent> {
106 self.agents
107 .iter()
108 .filter(|a| a.status == OxiosAgentGroupStatus::Failed)
109 .collect()
110 }
111
112 pub fn all_completed(&self) -> bool {
114 self.agents
115 .iter()
116 .all(|a| a.status == OxiosAgentGroupStatus::Completed)
117 }
118
119 pub fn any_failed(&self) -> bool {
121 self.agents
122 .iter()
123 .any(|a| a.status == OxiosAgentGroupStatus::Failed)
124 }
125
126 pub fn completion_pct(&self) -> f64 {
128 if self.agents.is_empty() {
129 return 0.0;
130 }
131 let completed = self
132 .agents
133 .iter()
134 .filter(|a| a.status == OxiosAgentGroupStatus::Completed)
135 .count();
136 completed as f64 / self.agents.len() as f64
137 }
138
139 pub fn combined_results(&self) -> String {
141 self.completed_agents()
142 .iter()
143 .filter_map(|a| a.result.as_ref())
144 .map(|r| r.as_str())
145 .collect::<Vec<_>>()
146 .join("\n\n")
147 }
148}
149
150#[cfg(test)]
151mod tests {
152 use super::*;
153
154 #[test]
155 fn test_group_new_splits_seed() {
156 let parent = Seed {
157 id: Uuid::new_v4(),
158 goal: "Test goal".into(),
159 constraints: vec!["constraint1".into()],
160 acceptance_criteria: vec!["Criterion".into()],
161 ontology: vec![],
162 created_at: Utc::now(),
163 generation: 0,
164 parent_seed_id: None,
165 cspace_hint: None,
166 original_request: String::new(),
167 output_schema: None,
168 project_id: None,
169 };
170
171 let descriptions = vec!["subtask 1".into(), "subtask 2".into()];
172 let group = OxiosAgentGroup::new(&parent, descriptions);
173
174 assert_eq!(group.agents.len(), 2);
175 assert!(group.pending_agents().len() == 2);
176 assert!(!group.all_completed());
177 assert_eq!(group.parent_seed_id, parent.id);
178 }
179
180 #[test]
181 fn test_completion_pct_empty_group() {
182 let parent = Seed {
183 id: Uuid::new_v4(),
184 goal: "Test".into(),
185 constraints: vec![],
186 acceptance_criteria: vec![],
187 ontology: vec![],
188 created_at: Utc::now(),
189 generation: 0,
190 parent_seed_id: None,
191 cspace_hint: None,
192 original_request: String::new(),
193 output_schema: None,
194 project_id: None,
195 };
196
197 let group = OxiosAgentGroup::new(&parent, vec![]);
198 assert!((group.completion_pct() - 0.0).abs() < f64::EPSILON);
199 }
200}