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 };
70 OxiosGroupAgent {
71 id: child_seed.id,
72 seed: child_seed,
73 status: OxiosAgentGroupStatus::Pending,
74 result: None,
75 }
76 })
77 .collect();
78
79 Self {
80 id: Uuid::new_v4(),
81 parent_seed_id: parent_seed.id,
82 agents,
83 }
84 }
85
86 pub fn pending_agents(&self) -> Vec<&OxiosGroupAgent> {
88 self.agents
89 .iter()
90 .filter(|a| a.status == OxiosAgentGroupStatus::Pending)
91 .collect()
92 }
93
94 pub fn completed_agents(&self) -> Vec<&OxiosGroupAgent> {
96 self.agents
97 .iter()
98 .filter(|a| a.status == OxiosAgentGroupStatus::Completed)
99 .collect()
100 }
101
102 pub fn failed_agents(&self) -> Vec<&OxiosGroupAgent> {
104 self.agents
105 .iter()
106 .filter(|a| a.status == OxiosAgentGroupStatus::Failed)
107 .collect()
108 }
109
110 pub fn all_completed(&self) -> bool {
112 self.agents
113 .iter()
114 .all(|a| a.status == OxiosAgentGroupStatus::Completed)
115 }
116
117 pub fn any_failed(&self) -> bool {
119 self.agents
120 .iter()
121 .any(|a| a.status == OxiosAgentGroupStatus::Failed)
122 }
123
124 pub fn completion_pct(&self) -> f64 {
126 if self.agents.is_empty() {
127 return 0.0;
128 }
129 let completed = self
130 .agents
131 .iter()
132 .filter(|a| a.status == OxiosAgentGroupStatus::Completed)
133 .count();
134 completed as f64 / self.agents.len() as f64
135 }
136
137 pub fn combined_results(&self) -> String {
139 self.completed_agents()
140 .iter()
141 .filter_map(|a| a.result.as_ref())
142 .map(|r| r.as_str())
143 .collect::<Vec<_>>()
144 .join("\n\n")
145 }
146}
147
148#[cfg(test)]
149mod tests {
150 use super::*;
151
152 #[test]
153 fn test_group_new_splits_seed() {
154 let parent = Seed {
155 id: Uuid::new_v4(),
156 goal: "Test goal".into(),
157 constraints: vec!["constraint1".into()],
158 acceptance_criteria: vec!["Criterion".into()],
159 ontology: vec![],
160 created_at: Utc::now(),
161 generation: 0,
162 parent_seed_id: None,
163 cspace_hint: None,
164 original_request: String::new(),
165 };
166
167 let descriptions = vec!["subtask 1".into(), "subtask 2".into()];
168 let group = OxiosAgentGroup::new(&parent, descriptions);
169
170 assert_eq!(group.agents.len(), 2);
171 assert!(group.pending_agents().len() == 2);
172 assert!(!group.all_completed());
173 assert_eq!(group.parent_seed_id, parent.id);
174 }
175
176 #[test]
177 fn test_completion_pct_empty_group() {
178 let parent = Seed {
179 id: Uuid::new_v4(),
180 goal: "Test".into(),
181 constraints: vec![],
182 acceptance_criteria: vec![],
183 ontology: vec![],
184 created_at: Utc::now(),
185 generation: 0,
186 parent_seed_id: None,
187 cspace_hint: None,
188 original_request: String::new(),
189 };
190
191 let group = OxiosAgentGroup::new(&parent, vec![]);
192 assert!((group.completion_pct() - 0.0).abs() < f64::EPSILON);
193 }
194}