batuta/recipes/
sovereign_deployment.rs1use crate::experiment::{ExperimentError, SovereignArtifact, SovereignDistribution};
4use crate::recipes::RecipeResult;
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct SovereignDeploymentConfig {
10 pub name: String,
12 pub version: String,
14 pub platforms: Vec<String>,
16 pub require_signatures: bool,
18 pub enable_nix_flake: bool,
20 pub offline_registry_path: Option<String>,
22}
23
24impl Default for SovereignDeploymentConfig {
25 fn default() -> Self {
26 Self {
27 name: "sovereign-model".to_string(),
28 version: "0.1.0".to_string(),
29 platforms: vec!["linux-x86_64".to_string()],
30 require_signatures: true,
31 enable_nix_flake: true,
32 offline_registry_path: None,
33 }
34 }
35}
36
37#[derive(Debug)]
39pub struct SovereignDeploymentRecipe {
40 config: SovereignDeploymentConfig,
41 distribution: SovereignDistribution,
42}
43
44impl SovereignDeploymentRecipe {
45 pub fn new(config: SovereignDeploymentConfig) -> Self {
47 let mut distribution = SovereignDistribution::new(&config.name, &config.version);
48 for platform in &config.platforms {
49 distribution.add_platform(platform);
50 }
51 Self { config, distribution }
52 }
53
54 fn add_artifact_impl(
56 &mut self,
57 name: impl Into<String>,
58 sha256: impl Into<String>,
59 size_bytes: u64,
60 artifact_type: crate::experiment::ArtifactType,
61 ) {
62 self.distribution.add_artifact(SovereignArtifact {
63 name: name.into(),
64 artifact_type,
65 sha256: sha256.into(),
66 size_bytes,
67 source_url: None,
68 });
69 }
70
71 pub fn add_model(
73 &mut self,
74 name: impl Into<String>,
75 sha256: impl Into<String>,
76 size_bytes: u64,
77 ) {
78 self.add_artifact_impl(name, sha256, size_bytes, crate::experiment::ArtifactType::Model);
79 }
80
81 pub fn add_binary(
83 &mut self,
84 name: impl Into<String>,
85 sha256: impl Into<String>,
86 size_bytes: u64,
87 ) {
88 self.add_artifact_impl(name, sha256, size_bytes, crate::experiment::ArtifactType::Binary);
89 }
90
91 pub fn add_dataset(
93 &mut self,
94 name: impl Into<String>,
95 sha256: impl Into<String>,
96 size_bytes: u64,
97 ) {
98 self.add_artifact_impl(name, sha256, size_bytes, crate::experiment::ArtifactType::Dataset);
99 }
100
101 pub fn sign_artifact(&mut self, artifact_name: impl Into<String>, key_id: impl Into<String>) {
103 let name = artifact_name.into();
104 let signature = format!("sig_placeholder_{}", &name);
106 self.distribution.signatures.push(crate::experiment::ArtifactSignature {
107 artifact_name: name,
108 algorithm: crate::experiment::SignatureAlgorithm::Ed25519,
109 signature,
110 key_id: key_id.into(),
111 });
112 }
113
114 pub fn build(&self) -> Result<RecipeResult, ExperimentError> {
116 if self.config.require_signatures {
118 self.distribution.validate_signatures()?;
119 }
120
121 let mut result = RecipeResult::success("sovereign-deployment");
122 result = result.with_metric("artifact_count", self.distribution.artifacts.len() as f64);
123 result =
124 result.with_metric("total_size_bytes", self.distribution.total_size_bytes() as f64);
125 result = result.with_metric("platform_count", self.distribution.platforms.len() as f64);
126
127 for artifact in &self.distribution.artifacts {
129 result = result.with_artifact(&artifact.name);
130 }
131
132 Ok(result)
133 }
134
135 pub fn distribution(&self) -> &SovereignDistribution {
137 &self.distribution
138 }
139
140 pub fn export_manifest(&self) -> Result<String, ExperimentError> {
142 serde_json::to_string_pretty(&self.distribution)
143 .map_err(|e| ExperimentError::StorageError(e.to_string()))
144 }
145}