1use std::sync::{Arc, RwLock};
10use std::time::{SystemTime, UNIX_EPOCH};
11
12use serde::{Deserialize, Serialize};
13
14fn generate_id(prefix: &str) -> String {
16 let now = SystemTime::now()
17 .duration_since(UNIX_EPOCH)
18 .unwrap_or_default();
19 format!("{}-{:x}", prefix, now.as_nanos())
20}
21
22#[derive(Clone, Debug, Deserialize, Serialize)]
24pub struct EvolutionSignal {
25 pub signal_id: String,
26 pub signal_type: SignalType,
27 pub source_task_id: String,
28 pub confidence: f32,
29 pub description: String,
30 pub metadata: serde_json::Value,
31}
32
33#[derive(Clone, Debug, Deserialize, Serialize)]
35#[serde(tag = "type", rename_all = "snake_case")]
36pub enum SignalType {
37 Performance {
39 metric: String,
40 improvement_potential: f32,
41 },
42 ErrorPattern { error_type: String, frequency: u32 },
44 ResourceOptimization {
46 resource_type: String,
47 current_usage: f32,
48 },
49 QualityIssue { issue_type: String, severity: f32 },
51 SuccessPattern { pattern: String, repeatability: f32 },
53}
54
55#[derive(Clone, Debug, Deserialize, Serialize)]
57pub struct MutationProposal {
58 pub proposal_id: String,
59 pub signal_ids: Vec<String>,
60 pub gene_id: String,
61 pub description: String,
62 pub estimated_impact: f32,
63 pub risk_level: MutationRiskLevel,
64 pub proposed_changes: serde_json::Value,
65}
66
67#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
69pub enum MutationRiskLevel {
70 Low,
71 Medium,
72 High,
73 Critical,
74}
75
76#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
78pub enum ProposalStatus {
79 Proposed,
80 Validating,
81 Validated,
82 Rejected { reason: String },
83 Approved,
84 Applied,
85 Failed { error: String },
86}
87
88#[derive(Clone, Debug, Deserialize, Serialize)]
90pub struct ValidationResult {
91 pub proposal_id: String,
92 pub passed: bool,
93 pub score: f32,
94 pub issues: Vec<ValidationIssue>,
95 pub simulation_results: Option<serde_json::Value>,
96}
97
98#[derive(Clone, Debug, Deserialize, Serialize)]
100pub struct ValidationIssue {
101 pub severity: IssueSeverity,
102 pub description: String,
103 pub location: Option<String>,
104}
105
106#[derive(Clone, Debug, Deserialize, Serialize)]
107pub enum IssueSeverity {
108 Info,
109 Warning,
110 Error,
111}
112
113#[derive(Clone, Debug, Deserialize, Serialize)]
115pub struct EvolverConfig {
116 #[serde(default = "default_enabled")]
118 pub enabled: bool,
119 #[serde(default = "default_min_confidence")]
121 pub min_signal_confidence: f32,
122 #[serde(default = "default_max_proposals")]
124 pub max_proposals_per_cycle: usize,
125 #[serde(default = "default_governor_required")]
127 pub governor_required_for_high_risk: bool,
128 #[serde(default = "default_validation_iterations")]
130 pub validation_iterations: u32,
131 #[serde(default = "default_auto_approve_threshold")]
133 pub auto_approve_threshold: f32,
134}
135
136fn default_enabled() -> bool {
137 false
138}
139fn default_min_confidence() -> f32 {
140 0.7
141}
142fn default_max_proposals() -> usize {
143 10
144}
145fn default_governor_required() -> bool {
146 true
147}
148fn default_validation_iterations() -> u32 {
149 3
150}
151fn default_auto_approve_threshold() -> f32 {
152 0.9
153}
154
155#[derive(Clone)]
157pub struct EvolverAutomation {
158 config: EvolverConfig,
159 signals: Arc<RwLock<Vec<EvolutionSignal>>>,
160 proposals: Arc<RwLock<Vec<MutationProposal>>>,
161 local_peer_id: String,
162}
163
164impl EvolverAutomation {
165 pub fn new(config: EvolverConfig, local_peer_id: String) -> Self {
167 Self {
168 config,
169 signals: Arc::new(RwLock::new(Vec::new())),
170 proposals: Arc::new(RwLock::new(Vec::new())),
171 local_peer_id,
172 }
173 }
174
175 pub fn add_signal(&self, signal: EvolutionSignal) {
177 if signal.confidence >= self.config.min_signal_confidence {
178 let mut signals = self.signals.write().unwrap();
179 signals.push(signal);
180 }
181 }
182
183 pub fn get_signals(&self) -> Vec<EvolutionSignal> {
185 self.signals.read().unwrap().clone()
186 }
187
188 pub fn clear_signals(&self, signal_ids: &[String]) {
190 let mut signals = self.signals.write().unwrap();
191 signals.retain(|s| !signal_ids.contains(&s.signal_id));
192 }
193
194 pub fn generate_proposals(&self) -> Vec<MutationProposal> {
196 let signals = self.signals.read().unwrap();
197 let mut proposals = Vec::new();
198
199 let mut by_gene: std::collections::HashMap<String, Vec<&EvolutionSignal>> =
201 std::collections::HashMap::new();
202 for signal in signals.iter() {
203 if let Some(gene_id) = signal.metadata.get("gene_id").and_then(|v| v.as_str()) {
204 by_gene.entry(gene_id.to_string()).or_default().push(signal);
205 }
206 }
207
208 let max = self.config.max_proposals_per_cycle;
210 for (gene_id, gene_signals) in by_gene.iter().take(max) {
211 let avg_confidence: f32 =
212 gene_signals.iter().map(|s| s.confidence).sum::<f32>() / gene_signals.len() as f32;
213 let risk_level = if avg_confidence > self.config.auto_approve_threshold {
214 MutationRiskLevel::Low
215 } else if avg_confidence > 0.5 {
216 MutationRiskLevel::Medium
217 } else {
218 MutationRiskLevel::High
219 };
220
221 let proposal = MutationProposal {
222 proposal_id: generate_id("proposal"),
223 signal_ids: gene_signals.iter().map(|s| s.signal_id.clone()).collect(),
224 gene_id: gene_id.clone(),
225 description: format!(
226 "{} mutation proposal based on {} signals",
227 gene_id,
228 gene_signals.len()
229 ),
230 estimated_impact: avg_confidence,
231 risk_level,
232 proposed_changes: serde_json::json!({
233 "signals": gene_signals.iter().map(|s| s.signal_type.clone()).collect::<Vec<_>>()
234 }),
235 };
236 proposals.push(proposal);
237 }
238
239 let mut stored = self.proposals.write().unwrap();
241 stored.extend(proposals.clone());
242
243 proposals
244 }
245
246 pub fn validate_proposal(&self, proposal_id: &str) -> ValidationResult {
248 let proposals = self.proposals.read().unwrap();
249 let proposal = proposals.iter().find(|p| p.proposal_id == proposal_id);
250
251 if let Some(p) = proposal {
252 let passed = p.risk_level != MutationRiskLevel::Critical;
254 let score = if passed { p.estimated_impact } else { 0.0 };
255
256 ValidationResult {
257 proposal_id: proposal_id.to_string(),
258 passed,
259 score,
260 issues: if !passed {
261 vec![ValidationIssue {
262 severity: IssueSeverity::Error,
263 description: "Critical risk mutations require governor approval"
264 .to_string(),
265 location: None,
266 }]
267 } else {
268 vec![]
269 },
270 simulation_results: Some(serde_json::json!({
271 "estimated_improvement": p.estimated_impact,
272 "risk_level": p.risk_level
273 })),
274 }
275 } else {
276 ValidationResult {
277 proposal_id: proposal_id.to_string(),
278 passed: false,
279 score: 0.0,
280 issues: vec![ValidationIssue {
281 severity: IssueSeverity::Error,
282 description: "Proposal not found".to_string(),
283 location: None,
284 }],
285 simulation_results: None,
286 }
287 }
288 }
289
290 pub fn approve_proposal(&self, proposal_id: &str) -> bool {
292 let mut proposals = self.proposals.write().unwrap();
293 if let Some(p) = proposals.iter_mut().find(|p| p.proposal_id == proposal_id) {
294 if self.config.governor_required_for_high_risk
296 && p.risk_level == MutationRiskLevel::Critical
297 {
298 return false;
299 }
300 return true;
301 }
302 false
303 }
304
305 pub fn get_proposals(&self, _status: Option<ProposalStatus>) -> Vec<MutationProposal> {
307 let proposals = self.proposals.read().unwrap();
308 proposals.clone()
309 }
310
311 pub fn config(&self) -> &EvolverConfig {
313 &self.config
314 }
315}
316
317pub struct SignalBuilder {
319 signal_type: Option<SignalType>,
320 source_task_id: String,
321 confidence: f32,
322 description: String,
323 metadata: serde_json::Value,
324}
325
326impl SignalBuilder {
327 pub fn new(source_task_id: String) -> Self {
328 Self {
329 signal_type: None,
330 source_task_id,
331 confidence: 0.0,
332 description: String::new(),
333 metadata: serde_json::json!({}),
334 }
335 }
336
337 pub fn signal_type(mut self, signal_type: SignalType) -> Self {
338 self.signal_type = Some(signal_type);
339 self
340 }
341
342 pub fn confidence(mut self, confidence: f32) -> Self {
343 self.confidence = confidence;
344 self
345 }
346
347 pub fn description(mut self, description: String) -> Self {
348 self.description = description;
349 self
350 }
351
352 pub fn metadata(mut self, metadata: serde_json::Value) -> Self {
353 self.metadata = metadata;
354 self
355 }
356
357 pub fn build(self) -> Option<EvolutionSignal> {
358 let signal_type = self.signal_type?;
359
360 Some(EvolutionSignal {
361 signal_id: generate_id("signal"),
362 signal_type,
363 source_task_id: self.source_task_id,
364 confidence: self.confidence,
365 description: self.description,
366 metadata: self.metadata,
367 })
368 }
369}
370
371impl Default for EvolverConfig {
372 fn default() -> Self {
373 Self {
374 enabled: false,
375 min_signal_confidence: default_min_confidence(),
376 max_proposals_per_cycle: default_max_proposals(),
377 governor_required_for_high_risk: default_governor_required(),
378 validation_iterations: default_validation_iterations(),
379 auto_approve_threshold: default_auto_approve_threshold(),
380 }
381 }
382}
383
384#[cfg(test)]
385mod tests {
386 use super::*;
387
388 #[test]
389 fn test_signal_builder() {
390 let signal = SignalBuilder::new("task-123".to_string())
391 .signal_type(SignalType::Performance {
392 metric: "latency".to_string(),
393 improvement_potential: 0.5,
394 })
395 .confidence(0.8)
396 .description("High latency detected".to_string())
397 .build();
398
399 assert!(signal.is_some());
400 let s = signal.unwrap();
401 assert_eq!(s.source_task_id, "task-123");
402 assert!(s.confidence >= 0.7); }
404
405 #[test]
406 fn test_proposal_generation() {
407 let config = EvolverConfig {
408 enabled: true,
409 min_signal_confidence: 0.5,
410 ..Default::default()
411 };
412 let evolver = EvolverAutomation::new(config, "local".to_string());
413
414 let signal = SignalBuilder::new("task-1".to_string())
416 .signal_type(SignalType::Performance {
417 metric: "throughput".to_string(),
418 improvement_potential: 0.8,
419 })
420 .confidence(0.9)
421 .metadata(serde_json::json!({ "gene_id": "gene-1" }))
422 .build()
423 .unwrap();
424
425 evolver.add_signal(signal);
426
427 let proposals = evolver.generate_proposals();
428 assert!(!proposals.is_empty());
429 }
430
431 #[test]
432 fn test_validation() {
433 let config = EvolverConfig::default();
434 let evolver = EvolverAutomation::new(config, "local".to_string());
435
436 let result = evolver.validate_proposal("non-existent");
437 assert!(!result.passed);
438 }
439}