1use serde::{Deserialize, Serialize};
14use std::sync::Arc;
15use thiserror::Error;
16
17use crate::core::{GeneCandidate, Selector, SelectorInput};
18use crate::evolver::{EvolutionSignal, MutationProposal, MutationRiskLevel, ValidationResult};
19
20#[derive(Clone, Debug, Serialize, Deserialize)]
22pub struct EvolutionPipelineConfig {
23 pub enable_detect: bool,
25 pub enable_select: bool,
26 pub enable_mutate: bool,
27 pub enable_execute: bool,
28 pub enable_validate: bool,
29 pub enable_evaluate: bool,
30 pub enable_solidify: bool,
31 pub enable_reuse: bool,
32
33 pub detect_timeout_secs: u64,
35 pub select_timeout_secs: u64,
36 pub mutate_timeout_secs: u64,
37 pub execute_timeout_secs: u64,
38 pub validate_timeout_secs: u64,
39 pub evaluate_timeout_secs: u64,
40 pub solidify_timeout_secs: u64,
41 pub reuse_timeout_secs: u64,
42
43 pub max_candidates: usize,
45
46 pub min_signal_confidence: f32,
48}
49
50impl Default for EvolutionPipelineConfig {
51 fn default() -> Self {
52 Self {
53 enable_detect: true,
54 enable_select: true,
55 enable_mutate: true,
56 enable_execute: true,
57 enable_validate: true,
58 enable_evaluate: true,
59 enable_solidify: true,
60 enable_reuse: true,
61 detect_timeout_secs: 30,
62 select_timeout_secs: 30,
63 mutate_timeout_secs: 60,
64 execute_timeout_secs: 300,
65 validate_timeout_secs: 60,
66 evaluate_timeout_secs: 30,
67 solidify_timeout_secs: 30,
68 reuse_timeout_secs: 30,
69 max_candidates: 10,
70 min_signal_confidence: 0.5,
71 }
72 }
73}
74
75#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
77pub enum PipelineStageState {
78 Pending,
80 Running,
82 Completed,
84 Failed(String),
86 Skipped(String),
88}
89
90pub struct PipelineContext {
92 pub task_input: serde_json::Value,
94 pub signals: Vec<EvolutionSignal>,
96 pub candidates: Vec<GeneCandidate>,
98 pub proposals: Vec<MutationProposal>,
100 pub execution_result: Option<serde_json::Value>,
102 pub validation_result: Option<ValidationResult>,
104 pub evaluation_result: Option<EvaluationResult>,
106 pub solidified_genes: Vec<String>,
108 pub reused_capsules: Vec<String>,
110}
111
112impl Default for PipelineContext {
113 fn default() -> Self {
114 Self {
115 task_input: serde_json::json!({}),
116 signals: Vec::new(),
117 candidates: Vec::new(),
118 proposals: Vec::new(),
119 execution_result: None,
120 validation_result: None,
121 evaluation_result: None,
122 solidified_genes: Vec::new(),
123 reused_capsules: Vec::new(),
124 }
125 }
126}
127
128#[derive(Clone, Debug, Serialize, Deserialize)]
130pub struct EvaluationResult {
131 pub score: f32,
132 pub improvements: Vec<String>,
133 pub regressions: Vec<String>,
134 pub recommendation: EvaluationRecommendation,
135}
136
137#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
138pub enum EvaluationRecommendation {
139 Accept,
140 Reject,
141 NeedsRevision,
142 RequiresHumanReview,
143}
144
145#[derive(Clone, Debug, Serialize, Deserialize)]
147pub struct PipelineResult {
148 pub success: bool,
150 pub stage_states: Vec<StageState>,
152 pub error: Option<String>,
154}
155
156#[derive(Clone, Debug, Serialize, Deserialize)]
158pub struct StageState {
159 pub stage_name: String,
160 pub state: PipelineStageState,
161 pub duration_ms: Option<u64>,
162}
163
164impl StageState {
165 pub fn new(name: &str) -> Self {
166 Self {
167 stage_name: name.to_string(),
168 state: PipelineStageState::Pending,
169 duration_ms: None,
170 }
171 }
172}
173
174#[derive(Error, Debug)]
176pub enum PipelineError {
177 #[error("Detect stage error: {0}")]
178 DetectError(String),
179
180 #[error("Select stage error: {0}")]
181 SelectError(String),
182
183 #[error("Mutate stage error: {0}")]
184 MutateError(String),
185
186 #[error("Execute stage error: {0}")]
187 ExecuteError(String),
188
189 #[error("Validate stage error: {0}")]
190 ValidateError(String),
191
192 #[error("Evaluate stage error: {0}")]
193 EvaluateError(String),
194
195 #[error("Solidify stage error: {0}")]
196 SolidifyError(String),
197
198 #[error("Reuse stage error: {0}")]
199 ReuseError(String),
200
201 #[error("Pipeline timeout: {0}")]
202 Timeout(String),
203}
204
205pub trait EvolutionPipeline: Send + Sync {
207 fn name(&self) -> &str;
209
210 fn config(&self) -> &EvolutionPipelineConfig;
212
213 fn execute(&self, context: PipelineContext) -> Result<PipelineResult, PipelineError>;
215
216 fn execute_stage(
218 &self,
219 stage: PipelineStage,
220 context: &mut PipelineContext,
221 ) -> Result<PipelineStageState, PipelineError>;
222}
223
224#[derive(Clone, Debug, Copy, PartialEq, Eq, Serialize, Deserialize)]
226pub enum PipelineStage {
227 Detect,
228 Select,
229 Mutate,
230 Execute,
231 Validate,
232 Evaluate,
233 Solidify,
234 Reuse,
235}
236
237impl PipelineStage {
238 pub fn as_str(&self) -> &'static str {
239 match self {
240 PipelineStage::Detect => "detect",
241 PipelineStage::Select => "select",
242 PipelineStage::Mutate => "mutate",
243 PipelineStage::Execute => "execute",
244 PipelineStage::Validate => "validate",
245 PipelineStage::Evaluate => "evaluate",
246 PipelineStage::Solidify => "solidify",
247 PipelineStage::Reuse => "reuse",
248 }
249 }
250
251 pub fn all() -> Vec<PipelineStage> {
252 vec![
253 PipelineStage::Detect,
254 PipelineStage::Select,
255 PipelineStage::Mutate,
256 PipelineStage::Execute,
257 PipelineStage::Validate,
258 PipelineStage::Evaluate,
259 PipelineStage::Solidify,
260 PipelineStage::Reuse,
261 ]
262 }
263}
264
265pub struct StandardEvolutionPipeline {
267 name: String,
268 config: EvolutionPipelineConfig,
269 selector: Arc<dyn Selector>,
270}
271
272impl StandardEvolutionPipeline {
273 pub fn new(config: EvolutionPipelineConfig, selector: Arc<dyn Selector>) -> Self {
274 Self {
275 name: "standard".to_string(),
276 config,
277 selector,
278 }
279 }
280}
281
282impl EvolutionPipeline for StandardEvolutionPipeline {
283 fn name(&self) -> &str {
284 &self.name
285 }
286
287 fn config(&self) -> &EvolutionPipelineConfig {
288 &self.config
289 }
290
291 fn execute(&self, mut context: PipelineContext) -> Result<PipelineResult, PipelineError> {
292 let mut stage_states = Vec::new();
293
294 if self.config.enable_detect {
296 let mut stage = StageState::new(PipelineStage::Detect.as_str());
297 stage.state = PipelineStageState::Running;
298 stage_states.push(stage);
299
300 stage_states.last_mut().unwrap().state = PipelineStageState::Completed;
303 } else {
304 stage_states.push(StageState {
305 stage_name: PipelineStage::Detect.as_str().to_string(),
306 state: PipelineStageState::Skipped("disabled".to_string()),
307 duration_ms: None,
308 });
309 }
310
311 if self.config.enable_select {
313 let mut stage = StageState::new(PipelineStage::Select.as_str());
314 stage.state = PipelineStageState::Running;
315 stage_states.push(stage);
316
317 let input = SelectorInput {
318 signals: context
319 .signals
320 .iter()
321 .map(|s| s.description.clone())
322 .collect(),
323 env: crate::core::EnvFingerprint {
324 rustc_version: String::new(),
325 cargo_lock_hash: String::new(),
326 target_triple: String::new(),
327 os: String::new(),
328 },
329 spec_id: None,
330 limit: self.config.max_candidates,
331 };
332
333 context.candidates = self.selector.select(&input);
334
335 stage_states.last_mut().unwrap().state = PipelineStageState::Completed;
336 } else {
337 stage_states.push(StageState {
338 stage_name: PipelineStage::Select.as_str().to_string(),
339 state: PipelineStageState::Skipped("disabled".to_string()),
340 duration_ms: None,
341 });
342 }
343
344 if self.config.enable_mutate {
346 let mut stage = StageState::new(PipelineStage::Mutate.as_str());
347 stage.state = PipelineStageState::Running;
348 stage_states.push(stage);
349
350 context.proposals = context
351 .candidates
352 .iter()
353 .enumerate()
354 .map(|(i, candidate)| MutationProposal {
355 proposal_id: format!("proposal_{}", i),
356 signal_ids: vec![],
357 gene_id: candidate.gene.id.clone(),
358 description: format!("Mutation for gene {}", candidate.gene.id),
359 estimated_impact: candidate.score,
360 risk_level: MutationRiskLevel::Medium,
361 proposed_changes: serde_json::json!({}),
362 })
363 .collect();
364
365 stage_states.last_mut().unwrap().state = PipelineStageState::Completed;
366 } else {
367 stage_states.push(StageState {
368 stage_name: PipelineStage::Mutate.as_str().to_string(),
369 state: PipelineStageState::Skipped("disabled".to_string()),
370 duration_ms: None,
371 });
372 }
373
374 if self.config.enable_execute {
376 let mut stage = StageState::new(PipelineStage::Execute.as_str());
377 stage.state = PipelineStageState::Running;
378 stage_states.push(stage);
379
380 context.execution_result = Some(serde_json::json!({
381 "status": "success",
382 "output": "Mutation executed successfully"
383 }));
384
385 stage_states.last_mut().unwrap().state = PipelineStageState::Completed;
386 } else {
387 stage_states.push(StageState {
388 stage_name: PipelineStage::Execute.as_str().to_string(),
389 state: PipelineStageState::Skipped("disabled".to_string()),
390 duration_ms: None,
391 });
392 }
393
394 if self.config.enable_validate {
396 let mut stage = StageState::new(PipelineStage::Validate.as_str());
397 stage.state = PipelineStageState::Running;
398 stage_states.push(stage);
399
400 if let Some(proposal) = context.proposals.first() {
402 context.validation_result = Some(ValidationResult {
403 proposal_id: proposal.proposal_id.clone(),
404 passed: true,
405 score: 0.8,
406 issues: vec![],
407 simulation_results: None,
408 });
409 }
410
411 stage_states.last_mut().unwrap().state = PipelineStageState::Completed;
412 } else {
413 stage_states.push(StageState {
414 stage_name: PipelineStage::Validate.as_str().to_string(),
415 state: PipelineStageState::Skipped("disabled".to_string()),
416 duration_ms: None,
417 });
418 }
419
420 if self.config.enable_evaluate {
422 let mut stage = StageState::new(PipelineStage::Evaluate.as_str());
423 stage.state = PipelineStageState::Running;
424 stage_states.push(stage);
425
426 context.evaluation_result = Some(EvaluationResult {
427 score: 0.8,
428 improvements: vec!["Mutation applied successfully".to_string()],
429 regressions: vec![],
430 recommendation: EvaluationRecommendation::Accept,
431 });
432
433 stage_states.last_mut().unwrap().state = PipelineStageState::Completed;
434 } else {
435 stage_states.push(StageState {
436 stage_name: PipelineStage::Evaluate.as_str().to_string(),
437 state: PipelineStageState::Skipped("disabled".to_string()),
438 duration_ms: None,
439 });
440 }
441
442 if self.config.enable_solidify {
444 let mut stage = StageState::new(PipelineStage::Solidify.as_str());
445 stage.state = PipelineStageState::Running;
446 stage_states.push(stage);
447
448 context.solidified_genes = context
449 .candidates
450 .iter()
451 .map(|c| c.gene.id.clone())
452 .collect();
453
454 stage_states.last_mut().unwrap().state = PipelineStageState::Completed;
455 } else {
456 stage_states.push(StageState {
457 stage_name: PipelineStage::Solidify.as_str().to_string(),
458 state: PipelineStageState::Skipped("disabled".to_string()),
459 duration_ms: None,
460 });
461 }
462
463 if self.config.enable_reuse {
465 let mut stage = StageState::new(PipelineStage::Reuse.as_str());
466 stage.state = PipelineStageState::Running;
467 stage_states.push(stage);
468
469 context.reused_capsules = context
471 .candidates
472 .iter()
473 .flat_map(|c| c.capsules.iter().map(|cap| cap.id.clone()))
474 .collect();
475
476 stage_states.last_mut().unwrap().state = PipelineStageState::Completed;
477 } else {
478 stage_states.push(StageState {
479 stage_name: PipelineStage::Reuse.as_str().to_string(),
480 state: PipelineStageState::Skipped("disabled".to_string()),
481 duration_ms: None,
482 });
483 }
484
485 Ok(PipelineResult {
486 success: true,
487 stage_states,
488 error: None,
489 })
490 }
491
492 fn execute_stage(
493 &self,
494 stage: PipelineStage,
495 context: &mut PipelineContext,
496 ) -> Result<PipelineStageState, PipelineError> {
497 match stage {
498 PipelineStage::Detect => {
499 Ok(PipelineStageState::Completed)
501 }
502 PipelineStage::Select => {
503 let input = SelectorInput {
504 signals: context
505 .signals
506 .iter()
507 .map(|s| s.description.clone())
508 .collect(),
509 env: crate::core::EnvFingerprint {
510 rustc_version: String::new(),
511 cargo_lock_hash: String::new(),
512 target_triple: String::new(),
513 os: String::new(),
514 },
515 spec_id: None,
516 limit: self.config.max_candidates,
517 };
518 context.candidates = self.selector.select(&input);
519 Ok(PipelineStageState::Completed)
520 }
521 PipelineStage::Mutate => {
522 context.proposals = context
523 .candidates
524 .iter()
525 .enumerate()
526 .map(|(i, candidate)| MutationProposal {
527 proposal_id: format!("proposal_{}", i),
528 signal_ids: vec![],
529 gene_id: candidate.gene.id.clone(),
530 description: format!("Mutation for gene {}", candidate.gene.id),
531 estimated_impact: candidate.score,
532 risk_level: MutationRiskLevel::Medium,
533 proposed_changes: serde_json::json!({}),
534 })
535 .collect();
536 Ok(PipelineStageState::Completed)
537 }
538 PipelineStage::Execute => {
539 context.execution_result = Some(serde_json::json!({
540 "status": "success"
541 }));
542 Ok(PipelineStageState::Completed)
543 }
544 PipelineStage::Validate => {
545 if let Some(proposal) = context.proposals.first() {
546 context.validation_result = Some(ValidationResult {
547 proposal_id: proposal.proposal_id.clone(),
548 passed: true,
549 score: 0.8,
550 issues: vec![],
551 simulation_results: None,
552 });
553 }
554 Ok(PipelineStageState::Completed)
555 }
556 PipelineStage::Evaluate => {
557 context.evaluation_result = Some(EvaluationResult {
558 score: 0.8,
559 improvements: vec![],
560 regressions: vec![],
561 recommendation: EvaluationRecommendation::Accept,
562 });
563 Ok(PipelineStageState::Completed)
564 }
565 PipelineStage::Solidify => {
566 context.solidified_genes = context
567 .candidates
568 .iter()
569 .map(|c| c.gene.id.clone())
570 .collect();
571 Ok(PipelineStageState::Completed)
572 }
573 PipelineStage::Reuse => {
574 context.reused_capsules = context
575 .candidates
576 .iter()
577 .flat_map(|c| c.capsules.iter().map(|cap| cap.id.clone()))
578 .collect();
579 Ok(PipelineStageState::Completed)
580 }
581 }
582 }
583}
584
585#[cfg(test)]
586mod tests {
587 use super::*;
588
589 #[test]
590 fn test_pipeline_config_default() {
591 let config = EvolutionPipelineConfig::default();
592 assert!(config.enable_detect);
593 assert!(config.enable_select);
594 assert!(config.enable_mutate);
595 }
596
597 #[test]
598 fn test_pipeline_stage_states() {
599 let config = EvolutionPipelineConfig {
600 enable_detect: false,
601 enable_select: true,
602 enable_mutate: false,
603 ..Default::default()
604 };
605
606 assert!(!config.enable_detect);
608 assert!(config.enable_select);
609 }
610}