qualia_core_db/inference/
inference_path_selector.rs1#![cfg(not(target_arch = "wasm32"))]
22
23use std::sync::atomic::{AtomicBool, Ordering};
24use std::sync::OnceLock;
25
26use crate::inference_modes::{set_inference_mode, InferenceMode};
27use crate::llm_bench::{
28 set_coop_gemv, set_ffn_fusion, set_kv_int8, set_resident_decode, set_resident_prefill,
29 set_resident_weights,
30};
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq)]
34pub enum ComputeLane {
35 PortableResident,
37 CudaAccelerated,
39}
40
41impl ComputeLane {
42 pub fn as_str(self) -> &'static str {
43 match self {
44 Self::PortableResident => "portable-resident",
45 Self::CudaAccelerated => "cuda-accelerated",
46 }
47 }
48}
49
50#[derive(Debug, Clone, Copy, PartialEq, Eq)]
52pub enum QuantProfile {
53 Int4SoaInt8Kv,
55 Int4SoaGraphHybrid,
57}
58
59impl QuantProfile {
60 pub fn as_str(self) -> &'static str {
61 match self {
62 Self::Int4SoaInt8Kv => "int4-soa+int8-kv",
63 Self::Int4SoaGraphHybrid => "int4-soa+int8-kv+graph",
64 }
65 }
66}
67
68#[derive(Debug, Clone, PartialEq)]
70pub struct InferencePathPlan {
71 pub wgpu_backend: Option<String>,
73 pub compute_lane: ComputeLane,
74 pub quant: QuantProfile,
75 pub prefill_prefer_tc: bool,
77 pub decode_is_gemv: bool,
79 pub rationale: String,
81 pub winning_circuit: Option<String>,
83 pub decode_proxy_tok_s: Option<f64>,
84 pub gemv_ms: Option<f64>,
85}
86
87static APPLIED: AtomicBool = AtomicBool::new(false);
88static LAST_PLAN: OnceLock<std::sync::Mutex<Option<InferencePathPlan>>> = OnceLock::new();
89
90fn last_plan_slot() -> &'static std::sync::Mutex<Option<InferencePathPlan>> {
91 LAST_PLAN.get_or_init(|| std::sync::Mutex::new(None))
92}
93
94pub fn last_inference_path_plan() -> Option<InferencePathPlan> {
96 last_plan_slot().lock().ok().and_then(|g| g.clone())
97}
98
99pub fn path_auto_enabled() -> bool {
101 match std::env::var("QUALIA_PATH_AUTO").ok().as_deref() {
102 Some("0") | Some("false") | Some("off") => false,
103 _ => true,
104 }
105}
106
107pub fn resolve_inference_path_plan() -> InferencePathPlan {
109 let mut rationale = Vec::new();
110
111 let env_backend = std::env::var("QUALIA_WGPU_BACKEND")
113 .ok()
114 .map(|s| s.trim().to_ascii_lowercase())
115 .filter(|s| !s.is_empty());
116
117 let (wgpu_backend, winning, decode_tok, gemv_ms) = if let Some(ref b) = env_backend {
118 rationale.push(format!("wgpu backend pinned by QUALIA_WGPU_BACKEND={b}"));
119 (Some(b.clone()), None, None, None)
120 } else if let Some(p) =
121 crate::hardware_passport::read_passport(&crate::hardware_passport::default_cache_path())
122 {
123 let best = p.matrix.best().cloned();
124 let token = p.preferred_inference_backend.clone().or_else(|| {
125 best.as_ref().and_then(|c| {
126 crate::hardware_passport::backend_env_token(&c.backend).map(str::to_string)
127 })
128 });
129 if let Some(ref c) = best {
130 rationale.push(format!(
131 "passport winner: {} [{}/{}] gemv={:.3}ms decode={:?}",
132 c.label,
133 format!("{:?}", c.kind),
134 c.backend,
135 c.ms_per_gemv,
136 c.decode_proxy_tok_s
137 ));
138 } else {
139 rationale.push("passport present but empty matrix".into());
140 }
141 (
142 token,
143 best.as_ref().map(|c| c.label.clone()),
144 best.as_ref().and_then(|c| c.decode_proxy_tok_s),
145 best.as_ref().map(|c| c.ms_per_gemv),
146 )
147 } else {
148 rationale.push(
149 "no hardware passport — run `qualia-cli llm passport --reprobe --decode-proxy <model> --apply-env-hint`"
150 .into(),
151 );
152 let def = if cfg!(target_os = "macos") || cfg!(target_os = "ios") {
154 rationale.push("default metal on Apple".into());
155 Some("metal".into())
156 } else {
157 None
158 };
159 (def, None, None, None)
160 };
161
162 let cuda_caps = crate::wgsl_forge::dispatch::caps().cuda;
164 let mode_pin = std::env::var("QUALIA_INFERENCE_MODE").ok();
165 let compute_lane = if let Some(ref m) = mode_pin {
166 if matches!(
167 m.to_ascii_lowercase().as_str(),
168 "cuda" | "cuda-tc" | "cudatc" | "tc" | "1"
169 ) {
170 rationale.push("compute lane pinned by QUALIA_INFERENCE_MODE=cuda".into());
171 ComputeLane::CudaAccelerated
172 } else {
173 rationale.push(format!("compute lane portable (mode pin {m})"));
174 ComputeLane::PortableResident
175 }
176 } else if cuda_caps && prefer_cuda_lane_heuristic() {
177 rationale.push(
178 "CUDA toolkit detected — lane=cuda-accelerated for prefill TC / Q4 device GEMV; decode still GEMV-primary"
179 .into(),
180 );
181 ComputeLane::CudaAccelerated
182 } else {
183 if cuda_caps {
184 rationale.push(
185 "CUDA present but portable-resident preferred (set QUALIA_INFERENCE_MODE=cuda to force)"
186 .into(),
187 );
188 } else {
189 rationale.push("no CUDA — portable resident (wgpu multi-weight in VRAM)".into());
190 }
191 ComputeLane::PortableResident
192 };
193
194 let rights = crate::inference_modes::rights_mode_enabled()
196 || matches!(
197 mode_pin
198 .as_deref()
199 .map(|s| s.to_ascii_lowercase())
200 .as_deref(),
201 Some("quant-graph")
202 | Some("graph")
203 | Some("hybrid")
204 | Some("2")
205 | Some("fast-verify")
206 | Some("fast_verify")
207 | Some("3")
208 );
209 if matches!(
211 mode_pin
212 .as_deref()
213 .map(|s| s.to_ascii_lowercase())
214 .as_deref(),
215 Some("fast-verify") | Some("fast_verify") | Some("ollama-like") | Some("3")
216 ) {
217 rationale.push(
218 "mode pin fast-verify: full-speed decode then post-turn CML/graph self-heal".into(),
219 );
220 }
221 let quant = if rights {
222 rationale.push("quant profile: INT4 SoA + INT8 KV + quant-graph hybrid".into());
223 QuantProfile::Int4SoaGraphHybrid
224 } else {
225 rationale.push("quant profile: INT4 SoA weights + INT8 KV (consumer bandwidth)".into());
226 QuantProfile::Int4SoaInt8Kv
227 };
228
229 let prefill_prefer_tc = matches!(compute_lane, ComputeLane::CudaAccelerated);
230 if prefill_prefer_tc {
231 rationale.push("prefill: prefer TC dense GEMM when m,n,k multiples of 16".into());
232 }
233 rationale
234 .push("decode: always GEMV (m=1) — tensor cores do not replace single-token GEMV".into());
235 rationale.push(
236 "multi-weight: wgpu resident plan keeps layer weights in VRAM (Vulkan/DX12/Metal); CUDA slab is optional densify path"
237 .into(),
238 );
239
240 InferencePathPlan {
241 wgpu_backend,
242 compute_lane,
243 quant,
244 prefill_prefer_tc,
245 decode_is_gemv: true,
246 rationale: rationale.join(" | "),
247 winning_circuit: winning,
248 decode_proxy_tok_s: decode_tok,
249 gemv_ms,
250 }
251}
252
253fn prefer_cuda_lane_heuristic() -> bool {
255 matches!(
258 std::env::var("QUALIA_PREFER_CUDA").ok().as_deref(),
259 Some("1") | Some("true") | Some("on")
260 )
261}
262
263pub fn apply_inference_path_plan(plan: &InferencePathPlan, force: bool) -> bool {
269 if force {
270 APPLIED.store(true, Ordering::SeqCst);
271 } else if APPLIED.swap(true, Ordering::SeqCst) {
272 return false; }
274
275 set_resident_decode(true);
277 set_resident_prefill(true);
278 set_resident_weights(true);
279 set_coop_gemv(true);
280 set_ffn_fusion(true);
281 set_kv_int8(true);
282
283 match plan.quant {
284 QuantProfile::Int4SoaInt8Kv => {
285 }
287 QuantProfile::Int4SoaGraphHybrid => {
288 if std::env::var("QUALIA_INFERENCE_MODE").is_err() {
289 if crate::inference_modes::rights_mode_enabled()
291 || matches!(
292 std::env::var("QUALIA_PREFER_FAST_VERIFY").ok().as_deref(),
293 Some("1") | Some("true")
294 )
295 {
296 set_inference_mode(InferenceMode::FastVerify);
297 } else {
298 set_inference_mode(InferenceMode::QuantGraph);
299 }
300 }
301 }
302 }
303
304 match plan.compute_lane {
305 ComputeLane::PortableResident => {
306 if std::env::var("QUALIA_INFERENCE_MODE").is_err()
307 && !matches!(plan.quant, QuantProfile::Int4SoaGraphHybrid)
308 {
309 set_inference_mode(InferenceMode::Portable);
310 }
311 }
312 ComputeLane::CudaAccelerated => {
313 if std::env::var("QUALIA_INFERENCE_MODE").is_err() {
314 set_inference_mode(InferenceMode::CudaTc);
315 }
316 crate::wgsl_forge::dispatch::ensure_cuda_runtime_path();
317 }
318 }
319
320 if std::env::var("QUALIA_WGPU_BACKEND").is_err() {
322 if let Some(ref b) = plan.wgpu_backend {
323 std::env::set_var("QUALIA_WGPU_BACKEND", b);
325 log::info!("path_select|set_env|QUALIA_WGPU_BACKEND={b}");
326 }
327 }
328
329 log::info!(
330 "path_select|applied|backend={:?}|lane={}|quant={}|prefill_tc={}|{}",
331 plan.wgpu_backend,
332 plan.compute_lane.as_str(),
333 plan.quant.as_str(),
334 plan.prefill_prefer_tc,
335 plan.rationale
336 );
337
338 if let Ok(mut g) = last_plan_slot().lock() {
339 *g = Some(plan.clone());
340 }
341 true
342}
343
344pub fn bootstrap_optimal_inference_path() -> InferencePathPlan {
346 let plan = resolve_inference_path_plan();
347 if path_auto_enabled() {
348 apply_inference_path_plan(&plan, false);
349 } else {
350 log::info!("path_select|skipped|QUALIA_PATH_AUTO=0|{}", plan.rationale);
351 if let Ok(mut g) = last_plan_slot().lock() {
352 *g = Some(plan.clone());
353 }
354 }
355 plan
356}
357
358pub fn format_path_plan(plan: &InferencePathPlan) -> String {
360 format!(
361 "InferencePathPlan\n wgpu_backend: {}\n compute_lane: {}\n quant: {}\n prefill_TC: {}\n decode: GEMV (m=1)\n winning_circuit: {}\n decode_proxy: {}\n gemv_ms: {}\n rationale:\n {}\n",
362 plan.wgpu_backend.as_deref().unwrap_or("(platform default)"),
363 plan.compute_lane.as_str(),
364 plan.quant.as_str(),
365 plan.prefill_prefer_tc,
366 plan.winning_circuit.as_deref().unwrap_or("—"),
367 plan.decode_proxy_tok_s
368 .map(|t| format!("{t:.2} tok/s"))
369 .unwrap_or_else(|| "—".into()),
370 plan.gemv_ms
371 .map(|m| format!("{m:.3}"))
372 .unwrap_or_else(|| "—".into()),
373 plan.rationale.replace(" | ", "\n "),
374 )
375}
376
377pub fn run_path_select_cli(reprobe: bool, apply: bool) -> InferencePathPlan {
379 if reprobe {
380 let _ = crate::hardware_passport::load_or_probe(
381 &crate::hardware_passport::default_cache_path(),
382 crate::hardware_passport::PASSPORT_GEMV_N,
383 );
384 }
385 let plan = resolve_inference_path_plan();
386 if apply {
387 apply_inference_path_plan(&plan, true);
388 if let Some(mut p) =
390 crate::hardware_passport::read_passport(&crate::hardware_passport::default_cache_path())
391 {
392 if let Some(ref b) = plan.wgpu_backend {
393 p.preferred_inference_backend = Some(b.clone());
394 }
395 let _ = crate::hardware_passport::write_passport(
396 &p,
397 &crate::hardware_passport::default_cache_path(),
398 );
399 }
400 }
401 plan
402}
403
404#[cfg(test)]
405mod tests {
406 use super::*;
407
408 #[test]
409 fn resolve_does_not_panic() {
410 let p = resolve_inference_path_plan();
411 assert!(p.decode_is_gemv);
412 assert!(!p.rationale.is_empty());
413 }
414
415 #[test]
416 fn format_contains_axes() {
417 let p = InferencePathPlan {
418 wgpu_backend: Some("dx12".into()),
419 compute_lane: ComputeLane::PortableResident,
420 quant: QuantProfile::Int4SoaInt8Kv,
421 prefill_prefer_tc: false,
422 decode_is_gemv: true,
423 rationale: "test".into(),
424 winning_circuit: Some("A2000".into()),
425 decode_proxy_tok_s: Some(2.5),
426 gemv_ms: Some(0.11),
427 };
428 let s = format_path_plan(&p);
429 assert!(s.contains("dx12"));
430 assert!(s.contains("portable-resident"));
431 assert!(s.contains("GEMV"));
432 }
433}