1use serde::{Deserialize, Serialize};
11use std::path::{Path, PathBuf};
12
13pub const EXECUTION_PROFILE_VERSION: u32 = 1;
15
16pub const EXECUTION_PROFILE_SUFFIX: &str = "execution-profile.json";
18
19#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
21pub struct ExecutionProfile {
22 pub version: u32,
23 pub written_unix_ms: u64,
25 pub source_import: String,
27 pub p64_path: String,
29 #[serde(default)]
31 pub q42_helper_path: String,
32 pub layout: String,
34 pub inference_mode: String,
36 pub backend: String,
38 pub toggles: ExecutionToggles,
40 pub metrics: ExecutionMetrics,
42 pub representation: RepresentationNotes,
44 #[serde(default)]
46 pub notes: Vec<String>,
47 #[serde(default)]
49 pub objectives: ObjectiveScores,
50}
51
52#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
54pub struct ExecutionToggles {
55 pub kv_int8: bool,
57 pub resident_decode: bool,
59 pub coop_gemv: bool,
61 pub ffn_fusion: bool,
63 pub ffn_f16: bool,
65 pub ternary_ffn: bool,
67}
68
69#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
71pub struct ExecutionMetrics {
72 pub decode_proxy_tok_s: Option<f64>,
74 pub decode_proxy_tokens: u32,
76 pub p64_bytes: u64,
78 pub coherence_ok: Option<bool>,
80 pub delta_ppl: Option<f64>,
82}
83
84#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
86pub struct RepresentationNotes {
87 pub weight_path: String,
89 pub kv_class: String,
91 pub ternary_158_available: bool,
93 pub f16_layout: bool,
95 pub soa_layout: bool,
97}
98
99#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
101pub struct ObjectiveScores {
102 pub throughput: Option<f64>,
104 pub format_fitness: Option<f64>,
106 pub correctness: Option<f64>,
108 pub grounding: Option<f64>,
110 pub governance: Option<f64>,
112 pub resource: Option<f64>,
114}
115
116impl ExecutionProfile {
117 pub fn path_for_p64(p64_path: &Path) -> PathBuf {
118 let parent = p64_path.parent().unwrap_or_else(|| Path::new("."));
119 let stem = p64_path
120 .file_stem()
121 .and_then(|s| s.to_str())
122 .unwrap_or("model");
123 parent.join(format!("{stem}.{EXECUTION_PROFILE_SUFFIX}"))
124 }
125
126 pub fn now_ms() -> u64 {
127 std::time::SystemTime::now()
128 .duration_since(std::time::UNIX_EPOCH)
129 .map(|d| d.as_millis() as u64)
130 .unwrap_or(0)
131 }
132
133 pub fn write_beside_p64(&self, p64_path: &Path) -> Result<PathBuf, String> {
135 let path = Self::path_for_p64(p64_path);
136 let json = serde_json::to_string_pretty(self).map_err(|e| format!("serialize: {e}"))?;
137 let tmp = path.with_extension("json.tmp");
138 std::fs::write(&tmp, json.as_bytes())
139 .map_err(|e| format!("write {}: {e}", tmp.display()))?;
140 std::fs::rename(&tmp, &path).or_else(|_| {
141 std::fs::write(&path, json.as_bytes())
142 .map_err(|e| format!("write {}: {e}", path.display()))
143 })?;
144 Ok(path)
145 }
146
147 pub fn load_beside_p64(p64_path: &Path) -> Result<Option<Self>, String> {
148 let path = Self::path_for_p64(p64_path);
149 if !path.is_file() {
150 return Ok(None);
151 }
152 let bytes = std::fs::read(&path).map_err(|e| format!("read {}: {e}", path.display()))?;
153 let p: Self =
154 serde_json::from_slice(&bytes).map_err(|e| format!("parse {}: {e}", path.display()))?;
155 Ok(Some(p))
156 }
157
158 pub fn from_explore_winner(
160 source_import: &str,
161 p64_path: &Path,
162 layout: &str,
163 inference_mode: &str,
164 backend: &str,
165 tok_s: f64,
166 tokens: u32,
167 toggle_label: &str,
168 ) -> Self {
169 let p64_bytes = std::fs::metadata(p64_path).map(|m| m.len()).unwrap_or(0);
170 let ffn_f16 = toggle_label.contains("ffn_f16=on");
171 let layout_l = layout.to_ascii_lowercase();
172 let helper = crate::q42::model_helper::helper_path_for_p64(p64_path);
173 let q42_helper_path = if helper.is_file() {
174 helper.display().to_string()
175 } else {
176 String::new()
177 };
178
179 #[cfg(any(not(target_arch = "wasm32"), feature = "wasm-llm"))]
182 let (kv_int8, resident_decode, coop_gemv, ffn_fusion, ternary_ffn) = (
183 crate::llm_bench::kv_int8_enabled(),
184 crate::llm_bench::resident_decode_enabled(),
185 crate::llm_bench::coop_gemv_enabled(),
186 crate::llm_bench::ffn_fusion_in_resident(),
187 crate::llm_bench::ternary_ffn_enabled(),
188 );
189 #[cfg(all(target_arch = "wasm32", not(feature = "wasm-llm")))]
190 let (kv_int8, resident_decode, coop_gemv, ffn_fusion, ternary_ffn) =
191 (false, false, false, false, false);
192
193 Self {
194 version: EXECUTION_PROFILE_VERSION,
195 written_unix_ms: Self::now_ms(),
196 source_import: source_import.to_string(),
197 p64_path: p64_path.display().to_string(),
198 q42_helper_path,
199 layout: layout.to_string(),
200 inference_mode: inference_mode.to_string(),
201 backend: backend.to_string(),
202 toggles: ExecutionToggles {
203 kv_int8,
204 resident_decode,
205 coop_gemv,
206 ffn_fusion,
207 ffn_f16,
208 ternary_ffn,
209 },
210 metrics: ExecutionMetrics {
211 decode_proxy_tok_s: Some(tok_s),
212 decode_proxy_tokens: tokens,
213 p64_bytes,
214 coherence_ok: None,
215 delta_ppl: None,
216 },
217 representation: RepresentationNotes {
218 weight_path: layout.to_string(),
219 kv_class: if kv_int8 { "int8".into() } else { "f32".into() },
220 ternary_158_available: false, f16_layout: layout_l.contains("f16"),
222 soa_layout: layout_l.contains("soa"),
223 },
224 notes: vec![
225 "Target: competitive native package (layout×mode×toggles) with coherent decode."
226 .into(),
227 "BitNet ternary ~1.58b (≈1.6 bits/weight, type 1158) is an FFN compression lever when present."
228 .into(),
229 format!("explore toggle label: {toggle_label}"),
230 ],
231 objectives: ObjectiveScores {
232 throughput: Some(tok_s),
233 format_fitness: None,
234 correctness: None,
235 grounding: None,
236 governance: None,
237 resource: None,
238 },
239 }
240 }
241
242 pub fn apply_env_script_ps1(&self) -> String {
244 let mut s = String::new();
245 s.push_str("# Qualia execution profile — apply before llm load / decode-proxy\n");
246 s.push_str(&format!(
247 "$env:QUALIA_INFERENCE_MODE='{}'\n",
248 self.inference_mode
249 ));
250 if self.backend != "auto" && !self.backend.is_empty() {
251 s.push_str(&format!("$env:QUALIA_WGPU_BACKEND='{}'\n", self.backend));
252 }
253 s.push_str(&format!(
254 "$env:QUALIA_LLM_KV_INT8='{}'\n",
255 if self.toggles.kv_int8 { "1" } else { "0" }
256 ));
257 s.push_str(&format!(
258 "$env:QUALIA_LLM_FFN_F16='{}'\n",
259 if self.toggles.ffn_f16 { "1" } else { "0" }
260 ));
261 s.push_str(&format!(
262 "$env:QUALIA_LLM_FFN_FUSION='{}'\n",
263 if self.toggles.ffn_fusion { "1" } else { "0" }
264 ));
265 s
266 }
267}
268
269#[cfg(test)]
270mod tests {
271 use super::*;
272 use std::io::Write;
273
274 #[test]
275 fn round_trip_beside_p64() {
276 let dir = std::env::temp_dir().join(format!("qualia-exec-profile-{}", std::process::id()));
277 let _ = std::fs::create_dir_all(&dir);
278 let p64 = dir.join("toy.p64");
279 {
280 let mut f = std::fs::File::create(&p64).unwrap();
281 f.write_all(b"p64\0toy").unwrap();
282 }
283 let mut prof = ExecutionProfile::from_explore_winner(
284 "toy.gguf", &p64, "f16", "portable", "auto", 12.5, 16, "baseline",
285 );
286 prof.representation.ternary_158_available = true;
287 let written = prof.write_beside_p64(&p64).expect("write");
288 assert!(written.is_file());
289 let loaded = ExecutionProfile::load_beside_p64(&p64)
290 .expect("load")
291 .expect("some");
292 assert_eq!(loaded.layout, "f16");
293 assert_eq!(loaded.metrics.decode_proxy_tok_s, Some(12.5));
294 assert!(loaded.representation.ternary_158_available);
295 let _ = std::fs::remove_dir_all(&dir);
296 }
297}