use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum ExperimentFramework {
MLflow,
WandB,
Neptune,
CometML,
Sacred,
Dvc,
}
impl ExperimentFramework {
pub fn name(&self) -> &'static str {
match self {
ExperimentFramework::MLflow => "MLflow",
ExperimentFramework::WandB => "Weights & Biases",
ExperimentFramework::Neptune => "Neptune.ai",
ExperimentFramework::CometML => "Comet ML",
ExperimentFramework::Sacred => "Sacred",
ExperimentFramework::Dvc => "DVC",
}
}
pub fn replacement(&self) -> &'static str {
match self {
ExperimentFramework::MLflow => "Entrenar + Batuta",
ExperimentFramework::WandB => "Entrenar + Trueno-Viz",
ExperimentFramework::Neptune => "Entrenar",
ExperimentFramework::CometML => "Entrenar + Presentar",
ExperimentFramework::Sacred => "Entrenar",
ExperimentFramework::Dvc => "Batuta + Trueno-DB",
}
}
pub fn all() -> Vec<ExperimentFramework> {
vec![
ExperimentFramework::MLflow,
ExperimentFramework::WandB,
ExperimentFramework::Neptune,
ExperimentFramework::CometML,
ExperimentFramework::Sacred,
ExperimentFramework::Dvc,
]
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum IntegrationType {
Replaces,
}
impl IntegrationType {
pub fn code(&self) -> &'static str {
match self {
IntegrationType::Replaces => "REP",
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FrameworkComponent {
pub name: String,
pub description: String,
pub replacement: String,
pub sub_components: Vec<String>,
}
impl FrameworkComponent {
fn new(name: &str, description: &str, replacement: &str) -> Self {
Self {
name: name.to_string(),
description: description.to_string(),
replacement: replacement.to_string(),
sub_components: Vec::new(),
}
}
fn with_subs(name: &str, description: &str, replacement: &str, subs: Vec<&str>) -> Self {
Self {
name: name.to_string(),
description: description.to_string(),
replacement: replacement.to_string(),
sub_components: subs.into_iter().map(String::from).collect(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FrameworkCategory {
pub name: String,
pub components: Vec<FrameworkComponent>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IntegrationMapping {
pub paiml_component: String,
pub python_component: String,
pub integration_type: IntegrationType,
pub category: String,
}
impl IntegrationMapping {
fn rep(paiml: &str, python: &str, category: &str) -> Self {
Self {
paiml_component: paiml.to_string(),
python_component: python.to_string(),
integration_type: IntegrationType::Replaces,
category: category.to_string(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExperimentTree {
pub framework: String,
pub replacement: String,
pub categories: Vec<FrameworkCategory>,
}
pub fn build_mlflow_tree() -> ExperimentTree {
ExperimentTree {
framework: "MLflow".to_string(),
replacement: "Entrenar + Batuta".to_string(),
categories: vec![
FrameworkCategory {
name: "Experiment Tracking".to_string(),
components: vec![
FrameworkComponent::with_subs(
"mlflow.start_run()",
"Run lifecycle management",
"Entrenar::ExperimentRun::new()",
vec!["log_param", "log_metric", "log_artifact"],
),
FrameworkComponent::new(
"mlflow.log_params()",
"Hyperparameter logging",
"ExperimentRun::log_param()",
),
FrameworkComponent::new(
"mlflow.log_metrics()",
"Metric logging",
"ExperimentRun::log_metric()",
),
FrameworkComponent::new(
"mlflow.set_tags()",
"Run tagging",
"ExperimentRun::tags",
),
],
},
FrameworkCategory {
name: "Model Registry".to_string(),
components: vec![
FrameworkComponent::with_subs(
"mlflow.register_model()",
"Model versioning",
"SovereignDistribution",
vec!["stage_transitions", "model_versions"],
),
FrameworkComponent::new(
"mlflow.pyfunc.log_model()",
"Model artifact storage",
"SovereignArtifact",
),
],
},
FrameworkCategory {
name: "Artifact Storage".to_string(),
components: vec![
FrameworkComponent::new(
"mlflow.log_artifact()",
"File storage",
"SovereignArtifact",
),
FrameworkComponent::new(
"S3/Azure/GCS backends",
"Cloud storage",
"Batuta deploy (sovereign)",
),
],
},
FrameworkCategory {
name: "Search & Query".to_string(),
components: vec![
FrameworkComponent::new(
"mlflow.search_runs()",
"Run search",
"ExperimentStorage::list_runs()",
),
FrameworkComponent::new(
"mlflow.search_experiments()",
"Experiment search",
"ExperimentStorage trait",
),
],
},
FrameworkCategory {
name: "GenAI / LLM".to_string(),
components: vec![
FrameworkComponent::with_subs(
"mlflow.tracing",
"LLM trace capture",
"Realizar::trace()",
vec!["OpenAI", "LangChain", "LlamaIndex"],
),
FrameworkComponent::new(
"mlflow.evaluate()",
"LLM evaluation",
"Entrenar::evaluate()",
),
FrameworkComponent::new(
"Prompt Registry",
"Prompt versioning",
"Realizar::PromptTemplate",
),
],
},
FrameworkCategory {
name: "Deployment".to_string(),
components: vec![
FrameworkComponent::new(
"mlflow models serve",
"Model serving",
"Batuta serve (GGUF)",
),
FrameworkComponent::new(
"MLflow Gateway",
"LLM gateway",
"Batuta serve + SpilloverRouter",
),
],
},
],
}
}
pub fn build_wandb_tree() -> ExperimentTree {
ExperimentTree {
framework: "Weights & Biases".to_string(),
replacement: "Entrenar + Trueno-Viz".to_string(),
categories: vec![
FrameworkCategory {
name: "Experiment Tracking".to_string(),
components: vec![
FrameworkComponent::new(
"wandb.init()",
"Run initialization",
"Entrenar::ExperimentRun::new()",
),
FrameworkComponent::new(
"wandb.log()",
"Metric logging",
"ExperimentRun::log_metric()",
),
FrameworkComponent::new(
"wandb.config",
"Hyperparameter config",
"ExperimentRun::hyperparameters",
),
],
},
FrameworkCategory {
name: "Visualization".to_string(),
components: vec![
FrameworkComponent::new("wandb.plot", "Custom plots", "Trueno-Viz::Chart"),
FrameworkComponent::new("Tables", "Data tables", "Trueno-Viz::DataGrid"),
FrameworkComponent::new(
"Media logging",
"Images/audio/video",
"Presentar::MediaView",
),
],
},
FrameworkCategory {
name: "Sweeps".to_string(),
components: vec![FrameworkComponent::with_subs(
"wandb.sweep()",
"Hyperparameter search",
"Entrenar::HyperparameterSearch",
vec!["grid", "random", "bayes"],
)],
},
FrameworkCategory {
name: "Artifacts".to_string(),
components: vec![FrameworkComponent::new(
"wandb.Artifact",
"Dataset/model versioning",
"SovereignArtifact",
)],
},
],
}
}
pub fn build_neptune_tree() -> ExperimentTree {
ExperimentTree {
framework: "Neptune.ai".to_string(),
replacement: "Entrenar".to_string(),
categories: vec![
FrameworkCategory {
name: "Experiment Tracking".to_string(),
components: vec![
FrameworkComponent::new(
"neptune.init_run()",
"Run initialization",
"Entrenar::ExperimentRun::new()",
),
FrameworkComponent::new(
"run[\"metric\"].log()",
"Metric logging",
"ExperimentRun::log_metric()",
),
],
},
FrameworkCategory {
name: "Metadata Store".to_string(),
components: vec![
FrameworkComponent::new(
"Namespace hierarchy",
"Nested metadata",
"ExperimentRun::hyperparameters (JSON)",
),
FrameworkComponent::new(
"System metrics",
"CPU/GPU/memory",
"EnergyMetrics + ComputeDevice",
),
],
},
],
}
}
pub fn build_dvc_tree() -> ExperimentTree {
ExperimentTree {
framework: "DVC".to_string(),
replacement: "Batuta + Trueno-DB".to_string(),
categories: vec![
FrameworkCategory {
name: "Data Versioning".to_string(),
components: vec![
FrameworkComponent::new(
"dvc add",
"Track data files",
"Trueno-DB::DatasetVersion",
),
FrameworkComponent::new(
"dvc push/pull",
"Remote storage",
"SovereignDistribution",
),
],
},
FrameworkCategory {
name: "Experiment Tracking".to_string(),
components: vec![
FrameworkComponent::new("dvc exp run", "Run experiments", "Batuta orchestrate"),
FrameworkComponent::new(
"dvc exp show",
"Compare experiments",
"Batuta experiment tree",
),
FrameworkComponent::new(
"dvc metrics",
"Metric tracking",
"ExperimentRun::metrics",
),
],
},
FrameworkCategory {
name: "Pipelines".to_string(),
components: vec![
FrameworkComponent::new("dvc.yaml", "Pipeline definition", "batuta.toml"),
FrameworkComponent::new(
"dvc repro",
"Reproduce pipeline",
"Batuta transpile + validate",
),
],
},
],
}
}
pub fn build_integration_mappings() -> Vec<IntegrationMapping> {
vec![
IntegrationMapping::rep(
"Entrenar::ExperimentRun",
"mlflow.start_run()",
"Experiment Tracking",
),
IntegrationMapping::rep(
"ExperimentRun::log_metric()",
"mlflow.log_metrics()",
"Experiment Tracking",
),
IntegrationMapping::rep(
"ExperimentRun::log_param()",
"mlflow.log_params()",
"Experiment Tracking",
),
IntegrationMapping::rep("ExperimentRun::tags", "mlflow.set_tags()", "Experiment Tracking"),
IntegrationMapping::rep("Entrenar::ExperimentRun", "wandb.init()", "Experiment Tracking"),
IntegrationMapping::rep(
"Entrenar::ExperimentRun",
"neptune.init_run()",
"Experiment Tracking",
),
IntegrationMapping::rep(
"SovereignDistribution",
"mlflow.register_model()",
"Model Registry",
),
IntegrationMapping::rep("SovereignArtifact", "mlflow.pyfunc.log_model()", "Model Registry"),
IntegrationMapping::rep("SovereignArtifact", "wandb.Artifact", "Model Registry"),
IntegrationMapping::rep("EnergyMetrics", "System metrics (wandb/neptune)", "Cost & Energy"),
IntegrationMapping::rep("CostMetrics", "N/A (not in MLflow)", "Cost & Energy"),
IntegrationMapping::rep(
"CostPerformanceBenchmark",
"N/A (Pareto frontier)",
"Cost & Energy",
),
IntegrationMapping::rep("ComputeDevice", "mlflow.system_metrics", "Cost & Energy"),
IntegrationMapping::rep("Trueno-Viz::Chart", "wandb.plot", "Visualization"),
IntegrationMapping::rep("Trueno-Viz::DataGrid", "wandb.Table", "Visualization"),
IntegrationMapping::rep("Presentar::Dashboard", "MLflow UI", "Visualization"),
IntegrationMapping::rep("Realizar::trace()", "mlflow.tracing", "LLM / GenAI"),
IntegrationMapping::rep("Entrenar::evaluate()", "mlflow.evaluate()", "LLM / GenAI"),
IntegrationMapping::rep(
"Realizar::PromptTemplate",
"MLflow Prompt Registry",
"LLM / GenAI",
),
IntegrationMapping::rep("Batuta serve", "mlflow models serve", "Deployment"),
IntegrationMapping::rep("SpilloverRouter", "MLflow Gateway", "Deployment"),
IntegrationMapping::rep("SovereignDistribution", "MLflow Docker/K8s", "Deployment"),
IntegrationMapping::rep("Trueno-DB::DatasetVersion", "dvc add", "Data Versioning"),
IntegrationMapping::rep("Batuta orchestrate", "dvc repro", "Data Versioning"),
IntegrationMapping::rep("ResearchArtifact", "N/A (ORCID/CRediT)", "Academic / Research"),
IntegrationMapping::rep("CitationMetadata", "N/A (BibTeX/CFF)", "Academic / Research"),
IntegrationMapping::rep("PreRegistration", "N/A (reproducibility)", "Academic / Research"),
]
}
fn format_category_components(
output: &mut String,
category: &FrameworkCategory,
cat_continuation: &str,
) {
for (comp_idx, component) in category.components.iter().enumerate() {
let is_last_comp = comp_idx == category.components.len() - 1;
let comp_prefix = if is_last_comp { "└──" } else { "├──" };
output.push_str(&format!(
"{}{} {} → {}\n",
cat_continuation, comp_prefix, component.name, component.replacement
));
if !component.sub_components.is_empty() {
let sub_cont = if is_last_comp {
format!("{} ", cat_continuation)
} else {
format!("{}│ ", cat_continuation)
};
for (sub_idx, sub) in component.sub_components.iter().enumerate() {
let sub_prefix = if sub_idx == component.sub_components.len() - 1 {
"└──"
} else {
"├──"
};
output.push_str(&format!("{}{} {}\n", sub_cont, sub_prefix, sub));
}
}
}
}
pub fn format_framework_tree(tree: &ExperimentTree) -> String {
let mut output = String::new();
output.push_str(&format!("{} (Python) → {} (Rust)\n", tree.framework, tree.replacement));
for (cat_idx, category) in tree.categories.iter().enumerate() {
let is_last_cat = cat_idx == tree.categories.len() - 1;
let cat_prefix = if is_last_cat { "└──" } else { "├──" };
let cat_continuation = if is_last_cat { " " } else { "│ " };
output.push_str(&format!("{} {}\n", cat_prefix, category.name));
format_category_components(&mut output, category, cat_continuation);
}
output
}
pub fn format_all_frameworks() -> String {
let mut output = String::new();
output.push_str("EXPERIMENT TRACKING FRAMEWORKS ECOSYSTEM\n");
output.push_str("========================================\n\n");
output.push_str(&format_framework_tree(&build_mlflow_tree()));
output.push('\n');
output.push_str(&format_framework_tree(&build_wandb_tree()));
output.push('\n');
output.push_str(&format_framework_tree(&build_neptune_tree()));
output.push('\n');
output.push_str(&format_framework_tree(&build_dvc_tree()));
output.push_str(&format!(
"\nSummary: {} Python frameworks replaced by sovereign Rust stack\n",
ExperimentFramework::all().len()
));
output
}
pub fn format_integration_mappings() -> String {
let mappings = build_integration_mappings();
let mut output = String::new();
output.push_str("PAIML REPLACEMENTS FOR PYTHON EXPERIMENT TRACKING\n");
output.push_str("=================================================\n\n");
let categories = [
"Experiment Tracking",
"Model Registry",
"Cost & Energy",
"Visualization",
"LLM / GenAI",
"Deployment",
"Data Versioning",
"Academic / Research",
];
for category in categories {
let cat_mappings: Vec<_> = mappings.iter().filter(|m| m.category == category).collect();
if cat_mappings.is_empty() {
continue;
}
output.push_str(&format!("{}\n", category.to_uppercase()));
for (idx, mapping) in cat_mappings.iter().enumerate() {
let is_last = idx == cat_mappings.len() - 1;
let prefix = if is_last { "└──" } else { "├──" };
output.push_str(&format!(
"{} [{}] {} ← {}\n",
prefix,
mapping.integration_type.code(),
mapping.paiml_component,
mapping.python_component
));
}
output.push('\n');
}
output.push_str("Legend: [REP]=Replaces (Python eliminated)\n\n");
output.push_str(&format!(
"Summary: {} Python components replaced by sovereign Rust alternatives\n",
mappings.len()
));
output.push_str(" Zero Python dependencies in production\n");
output
}
pub fn format_json(framework: Option<ExperimentFramework>, integration: bool) -> String {
if integration {
let mappings = build_integration_mappings();
serde_json::to_string_pretty(&mappings).unwrap_or_default()
} else {
match framework {
Some(ExperimentFramework::MLflow) => {
serde_json::to_string_pretty(&build_mlflow_tree()).unwrap_or_default()
}
Some(ExperimentFramework::WandB) => {
serde_json::to_string_pretty(&build_wandb_tree()).unwrap_or_default()
}
Some(ExperimentFramework::Neptune) => {
serde_json::to_string_pretty(&build_neptune_tree()).unwrap_or_default()
}
Some(ExperimentFramework::Dvc) => {
serde_json::to_string_pretty(&build_dvc_tree()).unwrap_or_default()
}
Some(fw @ (ExperimentFramework::CometML | ExperimentFramework::Sacred)) => {
let tree = ExperimentTree {
framework: fw.name().to_string(),
replacement: fw.replacement().to_string(),
categories: vec![],
};
serde_json::to_string_pretty(&tree).unwrap_or_default()
}
None => {
let trees = vec![
build_mlflow_tree(),
build_wandb_tree(),
build_neptune_tree(),
build_dvc_tree(),
];
serde_json::to_string_pretty(&trees).unwrap_or_default()
}
}
}
}
#[cfg(test)]
#[allow(non_snake_case)]
mod tests {
use super::*;
#[test]
fn test_EXP_TREE_001_framework_names() {
assert_eq!(ExperimentFramework::MLflow.name(), "MLflow");
assert_eq!(ExperimentFramework::WandB.name(), "Weights & Biases");
assert_eq!(ExperimentFramework::Neptune.name(), "Neptune.ai");
assert_eq!(ExperimentFramework::Dvc.name(), "DVC");
}
#[test]
fn test_EXP_TREE_002_framework_replacements() {
assert_eq!(ExperimentFramework::MLflow.replacement(), "Entrenar + Batuta");
assert_eq!(ExperimentFramework::WandB.replacement(), "Entrenar + Trueno-Viz");
assert_eq!(ExperimentFramework::Neptune.replacement(), "Entrenar");
assert_eq!(ExperimentFramework::Dvc.replacement(), "Batuta + Trueno-DB");
}
#[test]
fn test_EXP_TREE_003_all_frameworks() {
let all = ExperimentFramework::all();
assert_eq!(all.len(), 6);
}
#[test]
fn test_EXP_TREE_004_integration_type_code() {
assert_eq!(IntegrationType::Replaces.code(), "REP");
}
#[test]
fn test_EXP_TREE_005_mlflow_tree_structure() {
let tree = build_mlflow_tree();
assert_eq!(tree.framework, "MLflow");
assert_eq!(tree.replacement, "Entrenar + Batuta");
assert!(!tree.categories.is_empty());
let exp_tracking = tree.categories.iter().find(|c| c.name == "Experiment Tracking");
assert!(exp_tracking.is_some());
}
#[test]
fn test_EXP_TREE_006_mlflow_has_genai() {
let tree = build_mlflow_tree();
let genai = tree.categories.iter().find(|c| c.name == "GenAI / LLM");
assert!(genai.is_some());
let genai = genai.expect("unexpected failure");
assert!(genai.components.iter().any(|c| c.name == "mlflow.tracing"));
}
#[test]
fn test_EXP_TREE_007_wandb_tree_structure() {
let tree = build_wandb_tree();
assert_eq!(tree.framework, "Weights & Biases");
assert_eq!(tree.replacement, "Entrenar + Trueno-Viz");
let viz = tree.categories.iter().find(|c| c.name == "Visualization");
assert!(viz.is_some());
}
#[test]
fn test_EXP_TREE_008_neptune_tree_structure() {
let tree = build_neptune_tree();
assert_eq!(tree.framework, "Neptune.ai");
assert_eq!(tree.replacement, "Entrenar");
}
#[test]
fn test_EXP_TREE_009_dvc_tree_structure() {
let tree = build_dvc_tree();
assert_eq!(tree.framework, "DVC");
assert_eq!(tree.replacement, "Batuta + Trueno-DB");
let data_ver = tree.categories.iter().find(|c| c.name == "Data Versioning");
assert!(data_ver.is_some());
}
#[test]
fn test_EXP_TREE_010_integration_mappings_count() {
let mappings = build_integration_mappings();
assert!(mappings.len() >= 20);
}
#[test]
fn test_EXP_TREE_011_integration_mappings_categories() {
let mappings = build_integration_mappings();
let categories: std::collections::HashSet<_> =
mappings.iter().map(|m| m.category.as_str()).collect();
assert!(categories.contains("Experiment Tracking"));
assert!(categories.contains("Model Registry"));
assert!(categories.contains("Visualization"));
assert!(categories.contains("LLM / GenAI"));
}
#[test]
fn test_EXP_TREE_012_format_framework_tree() {
let tree = build_mlflow_tree();
let output = format_framework_tree(&tree);
assert!(output.contains("MLflow (Python) → Entrenar + Batuta (Rust)"));
assert!(output.contains("Experiment Tracking"));
assert!(output.contains("mlflow.start_run()"));
}
#[test]
fn test_EXP_TREE_013_format_all_frameworks() {
let output = format_all_frameworks();
assert!(output.contains("EXPERIMENT TRACKING FRAMEWORKS ECOSYSTEM"));
assert!(output.contains("MLflow"));
assert!(output.contains("Weights & Biases"));
assert!(output.contains("Neptune.ai"));
assert!(output.contains("DVC"));
assert!(output.contains("Summary:"));
}
#[test]
fn test_EXP_TREE_014_format_integration_mappings() {
let output = format_integration_mappings();
assert!(output.contains("PAIML REPLACEMENTS"));
assert!(output.contains("[REP]"));
assert!(output.contains("Entrenar::ExperimentRun"));
assert!(output.contains("mlflow.start_run()"));
assert!(output.contains("Legend:"));
}
#[test]
fn test_EXP_TREE_015_json_output_single() {
let json = format_json(Some(ExperimentFramework::MLflow), false);
assert!(json.contains("MLflow"));
assert!(json.contains("Entrenar"));
let parsed: Result<ExperimentTree, _> = serde_json::from_str(&json);
assert!(parsed.is_ok());
}
#[test]
fn test_EXP_TREE_016_json_output_all() {
let json = format_json(None, false);
let parsed: Result<Vec<ExperimentTree>, _> = serde_json::from_str(&json);
assert!(parsed.is_ok());
assert_eq!(parsed.expect("unexpected failure").len(), 4);
}
#[test]
fn test_EXP_TREE_017_json_output_integration() {
let json = format_json(None, true);
let parsed: Result<Vec<IntegrationMapping>, _> = serde_json::from_str(&json);
assert!(parsed.is_ok());
assert!(parsed.expect("unexpected failure").len() >= 20);
}
#[test]
fn test_EXP_TREE_018_all_mappings_have_replacements() {
let mappings = build_integration_mappings();
for mapping in &mappings {
assert_eq!(mapping.integration_type, IntegrationType::Replaces);
assert!(!mapping.paiml_component.is_empty());
assert!(!mapping.python_component.is_empty());
}
}
#[test]
fn test_EXP_TREE_019_academic_features() {
let mappings = build_integration_mappings();
let academic: Vec<_> =
mappings.iter().filter(|m| m.category == "Academic / Research").collect();
assert!(academic.len() >= 3);
assert!(academic.iter().any(|m| m.paiml_component.contains("ResearchArtifact")));
assert!(academic.iter().any(|m| m.paiml_component.contains("CitationMetadata")));
}
#[test]
fn test_EXP_TREE_020_cost_energy_features() {
let mappings = build_integration_mappings();
let cost_energy: Vec<_> =
mappings.iter().filter(|m| m.category == "Cost & Energy").collect();
assert!(cost_energy.len() >= 3);
assert!(cost_energy.iter().any(|m| m.paiml_component.contains("EnergyMetrics")));
assert!(cost_energy.iter().any(|m| m.paiml_component.contains("CostMetrics")));
}
}