use super::migration::ChainResult;
use super::{converter::to_runtime_genome, GenomeParser};
use crate::{EvoResult, RuntimeGenome};
use serde_json::Value;
use std::fs;
use std::path::Path;
pub fn load_genome_from_file<P: AsRef<Path>>(path: P) -> EvoResult<RuntimeGenome> {
let json_str = fs::read_to_string(path)?;
load_genome_from_json(&json_str)
}
pub fn load_genome_with_report_from_file<P: AsRef<Path>>(
path: P,
) -> EvoResult<(RuntimeGenome, ChainResult)> {
let json_str = fs::read_to_string(path)?;
load_genome_with_report(&json_str)
}
pub fn peek_quantization_precision<P: AsRef<Path>>(path: P) -> EvoResult<String> {
let json_str = fs::read_to_string(path)?;
let json_value: Value = serde_json::from_str(&json_str)
.map_err(|e| crate::types::EvoError::InvalidGenome(format!("Failed to parse JSON: {e}")))?;
let precision = json_value
.get("genome_physiology")
.and_then(|p| p.get("quantization_precision"))
.and_then(|q| q.as_str())
.unwrap_or("int8");
Ok(precision.to_lowercase())
}
pub fn load_genome_from_json(json_str: &str) -> EvoResult<RuntimeGenome> {
let (genome, _report) = load_genome_with_report(json_str)?;
Ok(genome)
}
pub fn load_genome_with_report(json_str: &str) -> EvoResult<(RuntimeGenome, ChainResult)> {
let json_value: Value = serde_json::from_str(json_str)
.map_err(|e| crate::types::EvoError::InvalidGenome(format!("Failed to parse JSON: {e}")))?;
let hierarchical_json = if is_flat_format(&json_value) {
crate::converter_flat_full::convert_flat_to_hierarchical_full(&json_value).map_err(|e| {
tracing::error!(target: "feagi-evo", "convert_flat_to_hierarchical_full failed: {}", e);
e
})?
} else {
json_value
};
let (migrated_json, report) = run_default_chain(hierarchical_json)?;
let migrated_json_str = serde_json::to_string(&migrated_json).map_err(|e| {
crate::types::EvoError::InvalidGenome(format!("Failed to serialize migrated genome: {e}"))
})?;
let parsed = GenomeParser::parse(&migrated_json_str).map_err(|e| {
tracing::error!(target: "feagi-evo", "GenomeParser::parse failed: {}", e);
e
})?;
let runtime_genome = to_runtime_genome(parsed, &migrated_json_str).map_err(|e| {
tracing::error!(target: "feagi-evo", "to_runtime_genome failed: {}", e);
e
})?;
Ok((runtime_genome, report))
}
fn run_default_chain(mut hierarchical_json: Value) -> EvoResult<(Value, ChainResult)> {
use crate::genome::default_chain_registry;
use crate::genome::migration::ChainRunner;
use crate::genome::schema::CURRENT_SCHEMA_VERSION;
let registry = default_chain_registry();
let runner = ChainRunner::new(®istry);
let result = runner
.run_to(&mut hierarchical_json, CURRENT_SCHEMA_VERSION)
.map_err(|e| crate::types::EvoError::InvalidGenome(format!("Genome chain failed: {e}")))?;
if !result.migrators_applied.is_empty() {
tracing::info!(
target: "feagi-evo",
"[GENOME-LOAD] Migrated v{} -> v{} via {:?}",
result.from_version.as_u32(),
result.to_version.as_u32(),
result.migrators_applied
);
for diag in &result.per_step_diagnostics {
for transform in &diag.transformations {
tracing::debug!(
target: "feagi-evo",
"[GENOME-LOAD] v{} -> v{}: {}",
diag.from_version.as_u32(),
diag.to_version.as_u32(),
transform
);
}
}
}
if !result.normalizers_applied.is_empty() {
for diag in &result.per_normalizer_diagnostics {
if !diag.is_clean() {
tracing::info!(
target: "feagi-evo",
"[GENOME-LOAD] Normalizer at v{} applied {} corrections",
diag.schema_version.as_u32(),
diag.transformations.len()
);
for transform in &diag.transformations {
tracing::debug!(target: "feagi-evo", "[GENOME-LOAD] {}", transform);
}
}
}
}
for warning in &result.advisory_warnings {
tracing::debug!(target: "feagi-evo", "[GENOME-LOAD] advisory: {}", warning);
}
if !result.is_blocking_clean() {
tracing::warn!(
target: "feagi-evo",
"[GENOME-LOAD] Genome v{} has {} blocking validator error(s); returning genome anyway (consumer decides)",
result.to_version.as_u32(),
result.blocking_errors.len()
);
for err in &result.blocking_errors {
tracing::warn!(target: "feagi-evo", "[GENOME-LOAD] blocking: {}", err);
}
}
Ok((hierarchical_json, result))
}
fn is_flat_format(genome_value: &Value) -> bool {
let blueprint = match genome_value.get("blueprint") {
Some(bp) => bp,
None => return false,
};
let blueprint_obj = match blueprint.as_object() {
Some(obj) => obj,
None => return false,
};
blueprint_obj
.keys()
.any(|key| key.starts_with("___") && key.contains('-') && key.len() > 20)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_load_minimal_genome() {
let json = r#"{
"genome_id": "test_genome",
"genome_title": "Test Genome",
"genome_description": "A test genome",
"version": "2.0",
"blueprint": {
"_power": {
"cortical_name": "Test Area",
"block_boundaries": [10, 10, 10],
"relative_coordinate": [0, 0, 0],
"cortical_type": "INTERCONNECT"
}
},
"brain_regions": {},
"neuron_morphologies": {},
"physiology": {
"simulation_timestep": 0.025,
"max_age": 10000000
},
"stats": {
"innate_cortical_area_count": 1,
"innate_neuron_count": 0,
"innate_synapse_count": 0
},
"signatures": {
"genome": "0000000000000000",
"blueprint": "0000000000000000",
"physiology": "0000000000000000"
},
"timestamp": 1234567890.0
}"#;
let genome = load_genome_from_json(json).unwrap();
assert_eq!(genome.metadata.genome_id, "test_genome");
assert_eq!(genome.metadata.version, "2.0");
assert_eq!(genome.cortical_areas.len(), 1);
assert_eq!(genome.physiology.simulation_timestep, 0.025);
}
#[test]
fn load_with_report_surfaces_chain_metadata() {
let json = r#"{
"genome_id": "t1",
"genome_title": "t",
"genome_description": "t",
"version": "2.0",
"blueprint": {},
"brain_regions": {},
"neuron_morphologies": {},
"physiology": { "simulation_timestep": 0.025, "max_age": 1 },
"stats": { "innate_cortical_area_count": 0, "innate_neuron_count": 0, "innate_synapse_count": 0 },
"signatures": { "genome": "0", "blueprint": "0", "physiology": "0" },
"timestamp": 0.0
}"#;
let (_genome, report) = load_genome_with_report(json).unwrap();
assert_eq!(report.from_version.as_u32(), 2);
assert_eq!(report.to_version.as_u32(), 3);
assert_eq!(report.migrators_applied, vec!["v2_to_v3"]);
assert!(report.is_blocking_clean());
}
#[test]
fn load_never_hard_fails_on_validator_errors() {
let json = r#"{
"genome_id": "t2",
"genome_title": "t",
"genome_description": "t",
"version": "3.0",
"genome_schema_version": 3,
"blueprint": {},
"brain_regions": {},
"neuron_morphologies": {},
"physiology": { "simulation_timestep": 0.025, "max_age": 1 },
"stats": { "innate_cortical_area_count": 0, "innate_neuron_count": 0, "innate_synapse_count": 0 },
"signatures": { "genome": "0", "blueprint": "0", "physiology": "0" },
"timestamp": 0.0
}"#;
let result = load_genome_with_report(json);
assert!(
result.is_ok(),
"load_genome_with_report must not turn validator output into Err"
);
}
}