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