#![allow(dead_code, unused_imports, unused_variables)]
pub mod ast_tools;
pub mod daemon;
pub mod fitness;
pub mod sandbox;
pub mod telemetry;
pub mod tournament;
use std::path::PathBuf;
use std::time::Duration;
pub const PROTECTED_PATHS: &[&str] = &[
"src/evolution/",
"src/safety/",
"system_tests/",
"benches/sab_",
];
#[derive(Debug, Clone)]
pub struct LlmConfig {
pub endpoint: String,
pub model: String,
pub api_key: Option<String>,
pub max_tokens: usize,
pub temperature: f32,
}
impl Default for LlmConfig {
fn default() -> Self {
Self {
endpoint: String::from("http://localhost:8080/v1"),
model: String::from("default"),
api_key: None,
max_tokens: 16384,
temperature: 0.7,
}
}
}
#[derive(Debug, Clone)]
pub struct EvolutionConfig {
pub generations: usize,
pub population_size: usize,
pub parallel_eval: usize,
pub checkpoint_interval: usize,
pub fitness_weights: FitnessWeights,
pub mutation_targets: MutationTargets,
pub safety: SafetyConfig,
pub llm: LlmConfig,
}
#[derive(Debug, Clone)]
pub struct FitnessWeights {
pub sab_score: f64,
pub token_efficiency: f64,
pub latency: f64,
pub test_coverage: f64,
pub binary_size: f64,
pub visual_quality: f64,
}
impl FitnessWeights {
pub fn composite(&self, metrics: &FitnessMetrics) -> f64 {
let normalized_tokens =
1.0 - (metrics.tokens_used as f64 / metrics.token_budget as f64).min(1.0);
let normalized_latency = 1.0 - (metrics.wall_clock_secs / metrics.timeout_secs).min(1.0);
let normalized_coverage = metrics.test_coverage_pct / 100.0;
let normalized_size = 1.0 - (metrics.binary_size_mb / metrics.max_binary_size_mb).min(1.0);
let normalized_visual = metrics.visual_score / 100.0;
self.sab_score * (metrics.sab_score / 100.0)
+ self.token_efficiency * normalized_tokens
+ self.latency * normalized_latency
+ self.test_coverage * normalized_coverage
+ self.binary_size * normalized_size
+ self.visual_quality * normalized_visual
}
}
impl Default for FitnessWeights {
fn default() -> Self {
Self {
sab_score: 0.50,
token_efficiency: 0.25,
latency: 0.15,
test_coverage: 0.05,
binary_size: 0.05,
visual_quality: 0.0,
}
}
}
#[derive(Debug, Clone)]
pub struct FitnessMetrics {
pub sab_score: f64,
pub tokens_used: u64,
pub token_budget: u64,
pub wall_clock_secs: f64,
pub timeout_secs: f64,
pub test_coverage_pct: f64,
pub binary_size_mb: f64,
pub max_binary_size_mb: f64,
pub tests_passed: usize,
pub tests_total: usize,
pub visual_score: f64,
}
#[derive(Debug, Clone)]
pub struct MutationTargets {
pub config_keys: Vec<String>,
pub prompt_logic: Vec<PathBuf>,
pub tool_code: Vec<PathBuf>,
pub cognitive: Vec<PathBuf>,
}
#[derive(Debug, Clone)]
pub struct SafetyConfig {
pub protected_files: Vec<String>,
pub min_test_count: usize,
pub max_binary_size_mb: f64,
pub rollback_on_any_test_failure: bool,
}
impl Default for SafetyConfig {
fn default() -> Self {
Self {
protected_files: PROTECTED_PATHS.iter().map(|s| s.to_string()).collect(),
min_test_count: 5000,
max_binary_size_mb: 50.0,
rollback_on_any_test_failure: true,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum GenerationRating {
Bloom,
Grow,
Wilt,
Frost,
}
impl std::fmt::Display for GenerationRating {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Bloom => write!(f, "BLOOM 🌸"),
Self::Grow => write!(f, "GROW 🌿"),
Self::Wilt => write!(f, "WILT 🥀"),
Self::Frost => write!(f, "FROST ❄️"),
}
}
}
pub fn is_protected(path: &std::path::Path) -> bool {
let path_str = path.to_string_lossy();
PROTECTED_PATHS.iter().any(|p| path_str.contains(p))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_protected_paths() {
assert!(is_protected(std::path::Path::new(
"src/evolution/daemon.rs"
)));
assert!(is_protected(std::path::Path::new("src/safety/sandbox.rs")));
assert!(is_protected(std::path::Path::new(
"system_tests/projecte2e/easy_calculator/"
)));
assert!(!is_protected(std::path::Path::new("src/agent/agent.rs")));
assert!(!is_protected(std::path::Path::new(
"src/tools/file_edit.rs"
)));
assert!(!is_protected(std::path::Path::new("src/memory.rs")));
}
#[test]
fn test_fitness_weights_default() {
let w = FitnessWeights::default();
let total = w.sab_score
+ w.token_efficiency
+ w.latency
+ w.test_coverage
+ w.binary_size
+ w.visual_quality;
assert!(
(total - 1.0).abs() < f64::EPSILON,
"Weights must sum to 1.0"
);
}
#[test]
fn test_composite_score_perfect() {
let w = FitnessWeights::default();
let metrics = FitnessMetrics {
sab_score: 100.0,
tokens_used: 0,
token_budget: 500_000,
wall_clock_secs: 0.0,
timeout_secs: 3600.0,
test_coverage_pct: 100.0,
binary_size_mb: 0.0,
max_binary_size_mb: 50.0,
tests_passed: 5200,
tests_total: 5200,
visual_score: 0.0,
};
let score = w.composite(&metrics);
assert!(
(score - 1.0).abs() < f64::EPSILON,
"Perfect metrics should yield 1.0"
);
}
#[test]
fn test_composite_score_ordering() {
let w = FitnessWeights::default();
let good = FitnessMetrics {
sab_score: 95.0,
tokens_used: 100_000,
token_budget: 500_000,
wall_clock_secs: 60.0,
timeout_secs: 3600.0,
test_coverage_pct: 85.0,
binary_size_mb: 10.0,
max_binary_size_mb: 50.0,
tests_passed: 5200,
tests_total: 5200,
visual_score: 0.0,
};
let bad = FitnessMetrics {
sab_score: 60.0,
tokens_used: 400_000,
token_budget: 500_000,
wall_clock_secs: 3000.0,
timeout_secs: 3600.0,
test_coverage_pct: 50.0,
binary_size_mb: 40.0,
max_binary_size_mb: 50.0,
tests_passed: 4000,
tests_total: 5200,
visual_score: 0.0,
};
assert!(w.composite(&good) > w.composite(&bad));
}
#[test]
fn test_generation_rating_display() {
assert_eq!(format!("{}", GenerationRating::Bloom), "BLOOM 🌸");
assert_eq!(format!("{}", GenerationRating::Frost), "FROST ❄️");
}
#[test]
fn test_composite_score_zero_budget() {
let w = FitnessWeights::default();
let metrics = FitnessMetrics {
sab_score: 50.0,
tokens_used: 10,
token_budget: 1, wall_clock_secs: 100.0,
timeout_secs: 3600.0,
test_coverage_pct: 80.0,
binary_size_mb: 10.0,
max_binary_size_mb: 50.0,
tests_passed: 100,
tests_total: 100,
visual_score: 0.0,
};
let score = w.composite(&metrics);
assert!(score >= 0.0, "Score should be non-negative");
assert!(score <= 1.0, "Score should be <= 1.0");
}
#[test]
fn test_composite_score_custom_weights() {
let w = FitnessWeights {
sab_score: 1.0,
token_efficiency: 0.0,
latency: 0.0,
test_coverage: 0.0,
binary_size: 0.0,
visual_quality: 0.0,
};
let metrics = FitnessMetrics {
sab_score: 75.0,
tokens_used: 999_999,
token_budget: 100,
wall_clock_secs: 99999.0,
timeout_secs: 1.0,
test_coverage_pct: 0.0,
binary_size_mb: 999.0,
max_binary_size_mb: 1.0,
tests_passed: 0,
tests_total: 100,
visual_score: 0.0,
};
let score = w.composite(&metrics);
assert!(
(score - 0.75).abs() < f64::EPSILON,
"Score should be 0.75, got {}",
score
);
}
#[test]
fn test_is_protected_empty_path() {
assert!(!is_protected(std::path::Path::new("")));
}
#[test]
fn test_is_protected_partial_match() {
assert!(!is_protected(std::path::Path::new(
"src/evolutionary/something.rs"
)));
assert!(is_protected(std::path::Path::new(
"src/evolution/something.rs"
)));
}
#[test]
fn test_safety_config_default() {
let cfg = SafetyConfig::default();
assert_eq!(cfg.min_test_count, 5000);
assert_eq!(cfg.max_binary_size_mb, 50.0);
assert!(cfg.rollback_on_any_test_failure);
assert_eq!(cfg.protected_files.len(), PROTECTED_PATHS.len());
for p in PROTECTED_PATHS {
assert!(
cfg.protected_files.contains(&p.to_string()),
"Missing protected path: {}",
p
);
}
}
#[test]
fn test_generation_rating_all_variants() {
assert_eq!(format!("{}", GenerationRating::Grow), "GROW 🌿");
assert_eq!(format!("{}", GenerationRating::Wilt), "WILT 🥀");
}
#[test]
fn test_composite_score_worst_case() {
let w = FitnessWeights::default();
let metrics = FitnessMetrics {
sab_score: 0.0,
tokens_used: 500_000,
token_budget: 500_000,
wall_clock_secs: 3600.0,
timeout_secs: 3600.0,
test_coverage_pct: 0.0,
binary_size_mb: 50.0,
max_binary_size_mb: 50.0,
tests_passed: 0,
tests_total: 5000,
visual_score: 0.0,
};
let score = w.composite(&metrics);
assert!(
score.abs() < f64::EPSILON,
"Worst metrics should yield 0.0, got {}",
score
);
}
}