Skip to main content

qualia_core_db/inference/
application_profile.rs

1//! Application profiles — **how** inference is used, not only **which** GPU path.
2//!
3//! Timothy (2026-07-10): on a local device, work need not be live. High-stakes
4//! multi-system health eval / differential analysis can run overnight and deliver
5//! a verified HTML (or email body). Different applications want different modes:
6//!
7//! | Profile | Latency | Mid-decode | Post-turn | Decode budget | Timeout |
8//! |---------|---------|------------|-----------|---------------|---------|
9//! | **Interactive** | low | optional | light | 256 | 30s |
10//! | **LiveFast** | lowest | off (FastVerify) | graph heal | 256 | 30s |
11//! | **BatchOvernight** | irrelevant | off | full HTML+CML | 2048 | 8h |
12//!
13//! No Ollama API: all profiles stay in-process Qualia (P64 + resident GEMV + graph).
14
15use std::sync::atomic::{AtomicU8, Ordering};
16
17use crate::inference_modes::{set_inference_mode, InferenceMode};
18#[cfg(not(target_arch = "wasm32"))]
19use crate::llm_bench::{set_decode_budget_override, set_inference_timeout_override_ms};
20
21#[cfg(target_arch = "wasm32")]
22fn set_decode_budget_override(_n: u32) {}
23#[cfg(target_arch = "wasm32")]
24fn set_inference_timeout_override_ms(_ms: u64) {}
25
26#[repr(u8)]
27#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
28pub enum ApplicationProfile {
29    /// Chat / UI — path selector + portable defaults.
30    Interactive = 0,
31    /// Streaming-feel: FastVerify (generate then post-heal).
32    LiveFast = 1,
33    /// Overnight / high-stakes batch: long budget, HTML verification surface.
34    BatchOvernight = 2,
35}
36
37impl ApplicationProfile {
38    pub const ALL: [ApplicationProfile; 3] = [
39        ApplicationProfile::Interactive,
40        ApplicationProfile::LiveFast,
41        ApplicationProfile::BatchOvernight,
42    ];
43
44    pub fn as_str(self) -> &'static str {
45        match self {
46            Self::Interactive => "interactive",
47            Self::LiveFast => "live-fast",
48            Self::BatchOvernight => "batch",
49        }
50    }
51
52    pub fn parse(s: &str) -> Option<Self> {
53        match s.trim().to_ascii_lowercase().as_str() {
54            "interactive" | "chat" | "ui" | "0" => Some(Self::Interactive),
55            "live-fast" | "live_fast" | "live" | "fast" | "1" => Some(Self::LiveFast),
56            "batch" | "batch-overnight" | "overnight" | "offline" | "async" | "email" | "2" => {
57                Some(Self::BatchOvernight)
58            }
59            _ => None,
60        }
61    }
62
63    pub fn description(self) -> &'static str {
64        match self {
65            Self::Interactive => {
66                "interactive chat: path-selected GPU backend, 256-tok budget, 30s wall-clock"
67            }
68            Self::LiveFast => {
69                "live-fast: FastVerify (uninterrupted decode → post graph/CML heal), still local"
70            }
71            Self::BatchOvernight => {
72                "batch/overnight: up to 2048 tokens, 8h wall-clock, HTML+CML verify — multi-system eval, email-ready"
73            }
74        }
75    }
76}
77
78static PROFILE: AtomicU8 = AtomicU8::new(ApplicationProfile::Interactive as u8);
79
80pub fn active_application_profile() -> ApplicationProfile {
81    if let Ok(s) = std::env::var("QUALIA_APP_PROFILE") {
82        if let Some(p) = ApplicationProfile::parse(&s) {
83            return p;
84        }
85    }
86    match PROFILE.load(Ordering::Relaxed) {
87        1 => ApplicationProfile::LiveFast,
88        2 => ApplicationProfile::BatchOvernight,
89        _ => ApplicationProfile::Interactive,
90    }
91}
92
93/// Apply profile: inference mode, budgets, timeouts, HTML return for batch.
94pub fn set_application_profile(profile: ApplicationProfile) {
95    PROFILE.store(profile as u8, Ordering::Relaxed);
96    apply_application_profile(profile);
97    log::info!("APP_PROFILE|{}|{}", profile.as_str(), profile.description());
98}
99
100pub fn apply_application_profile(profile: ApplicationProfile) {
101    match profile {
102        ApplicationProfile::Interactive => {
103            set_decode_budget_override(0); // production 256
104            set_inference_timeout_override_ms(0); // 30s default
105            if std::env::var("QUALIA_INFERENCE_MODE").is_err() {
106                set_inference_mode(InferenceMode::Portable);
107            }
108        }
109        ApplicationProfile::LiveFast => {
110            set_decode_budget_override(0);
111            set_inference_timeout_override_ms(0);
112            if std::env::var("QUALIA_INFERENCE_MODE").is_err() {
113                set_inference_mode(InferenceMode::FastVerify);
114            }
115        }
116        ApplicationProfile::BatchOvernight => {
117            // Long-form reasoning for differential / multi-system jobs.
118            set_decode_budget_override(2048);
119            set_inference_timeout_override_ms(8 * 60 * 60 * 1000); // 8 hours
120                                                                   // Always post-verify + HTML surface for email / archival.
121            std::env::set_var("QUALIA_RETURN_VERIFY_HTML", "1");
122            if std::env::var("QUALIA_INFERENCE_MODE").is_err() {
123                set_inference_mode(InferenceMode::FastVerify);
124            }
125            // Ensure fact graph is warm.
126            let n = crate::quant_graph_grounding::seed_facts_from_bundled();
127            log::info!("APP_PROFILE|batch|facts_seeded|{n}|budget=2048|timeout=8h|html=1");
128        }
129    }
130}
131
132/// Bootstrap from env `QUALIA_APP_PROFILE` (call early with path selector).
133pub fn bootstrap_application_profile() -> ApplicationProfile {
134    let p = active_application_profile();
135    apply_application_profile(p);
136    p
137}
138
139#[cfg(test)]
140mod tests {
141    use super::*;
142
143    #[test]
144    fn parse_profiles() {
145        assert_eq!(
146            ApplicationProfile::parse("overnight"),
147            Some(ApplicationProfile::BatchOvernight)
148        );
149        assert_eq!(
150            ApplicationProfile::parse("live-fast"),
151            Some(ApplicationProfile::LiveFast)
152        );
153    }
154}