pub mod auto_quant;
pub mod fingerprint;
pub mod heuristics;
pub mod ruvector;
use serde::{Deserialize, Serialize};
use thiserror::Error;
use tracing::{info, warn};
use self::auto_quant::{AutoQuantConstraints, AutoQuantPlan};
use self::fingerprint::ModelFingerprint;
#[allow(unused_imports)]
use self::heuristics::HeuristicResult;
use crate::core::hardware::HardwareProfile;
#[derive(Error, Debug)]
#[allow(dead_code)]
pub enum IntelligenceError {
#[error("Hardware profiling failed: {0}")]
Hardware(#[from] crate::core::hardware::HardwareError),
#[error("Model fingerprinting failed: {0}")]
Fingerprint(#[from] fingerprint::FingerprintError),
#[error("Heuristic resolution failed: {0}")]
Heuristics(#[from] heuristics::HeuristicsError),
#[error("RuVector is not accessible: {reason}. Required to store learnings. Run `hf2q doctor` to diagnose.")]
RuVectorUnavailable { reason: String },
#[error("RuVector error: {0}")]
RuVector(#[from] ruvector::RuVectorError),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResolvedConfig {
pub quant_method: String,
pub bits: u8,
pub group_size: usize,
pub confidence: f64,
pub source: ResolvedSource,
pub reasoning: String,
pub hardware: HardwareProfile,
pub fingerprint: ModelFingerprint,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum ResolvedSource {
RuVectorExact,
RuVectorSimilar,
Heuristic,
}
impl std::fmt::Display for ResolvedSource {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::RuVectorExact => write!(f, "stored (exact match)"),
Self::RuVectorSimilar => write!(f, "stored (similar match)"),
Self::Heuristic => write!(f, "heuristic"),
}
}
}
pub struct AutoResolver;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OutputFormatHint {
Gguf,
Safetensors,
}
impl AutoResolver {
#[allow(dead_code)]
pub fn resolve(
hardware: &HardwareProfile,
fingerprint: &ModelFingerprint,
ruvector_db: Option<&ruvector::RuVectorDb>,
) -> Result<ResolvedConfig, IntelligenceError> {
Self::resolve_with_format(hardware, fingerprint, ruvector_db, None)
}
pub fn resolve_with_format(
hardware: &HardwareProfile,
fingerprint: &ModelFingerprint,
ruvector_db: Option<&ruvector::RuVectorDb>,
format: Option<OutputFormatHint>,
) -> Result<ResolvedConfig, IntelligenceError> {
info!(
hardware_id = %hardware.stable_id(),
model_id = %fingerprint.stable_id(),
model = %fingerprint,
total_memory_gb = hardware.total_memory_gb(),
available_memory_gb = hardware.available_memory_gb(),
"Auto mode: resolving optimal quantization"
);
if let Some(db) = ruvector_db {
match db.query_best_config(hardware, fingerprint) {
Ok(Some(stored)) => {
info!(
method = %stored.quant_method,
confidence = stored.confidence,
source = %stored.source,
"Auto mode: using stored RuVector result"
);
return Ok(stored);
}
Ok(None) => {
warn!(
"No prior conversion data for this hardware+model combination. \
Using heuristics. Results will be stored after conversion."
);
}
Err(e) => {
warn!(
error = %e,
"RuVector query failed, falling back to heuristics"
);
}
}
}
let constraints = AutoQuantConstraints::default();
match auto_quant::resolve_auto_plan(hardware, fingerprint, &constraints) {
Ok(plan) => {
let (method, reasoning_suffix) = match format {
Some(OutputFormatHint::Gguf) => {
if plan.base_bits <= 6 && fingerprint.total_params >= 3_000_000_000 {
(
"apex".to_string(),
" Format: GGUF (K-quant types available, Apex recommended).",
)
} else {
(plan.quant_method.clone(), " Format: GGUF.")
}
}
Some(OutputFormatHint::Safetensors) => {
if plan.quant_method == "apex" {
(format!("mixed-{}-6", plan.base_bits.max(2)),
" Format: safetensors (K-quant types not available, using mixed-bit).")
} else {
(plan.quant_method.clone(), " Format: safetensors.")
}
}
None => (plan.quant_method.clone(), ""),
};
let resolved = ResolvedConfig {
quant_method: method,
bits: plan.base_bits,
group_size: plan.group_size,
confidence: plan.confidence,
source: ResolvedSource::Heuristic,
reasoning: format!("{}{}", plan.reasoning, reasoning_suffix),
hardware: hardware.clone(),
fingerprint: fingerprint.clone(),
};
info!(
method = %resolved.quant_method,
confidence = resolved.confidence,
source = "auto_quant",
overrides = plan.component_overrides.len(),
"Auto mode: using auto_quant plan"
);
return Ok(resolved);
}
Err(e) => {
warn!(
error = %e,
"Auto-quant plan failed, falling back to basic heuristic"
);
}
}
let heuristic = heuristics::select_quant_with_format(hardware, fingerprint, format)?;
let resolved = ResolvedConfig {
quant_method: heuristic.quant_method,
bits: heuristic.bits,
group_size: heuristic.group_size,
confidence: heuristic.confidence,
source: ResolvedSource::Heuristic,
reasoning: heuristic.reasoning,
hardware: hardware.clone(),
fingerprint: fingerprint.clone(),
};
info!(
method = %resolved.quant_method,
confidence = resolved.confidence,
source = "heuristic",
"Auto mode: using heuristic recommendation"
);
Ok(resolved)
}
pub fn generate_plan(
hardware: &HardwareProfile,
fingerprint: &ModelFingerprint,
) -> Option<AutoQuantPlan> {
let constraints = AutoQuantConstraints::default();
match auto_quant::resolve_auto_plan(hardware, fingerprint, &constraints) {
Ok(plan) => Some(plan),
Err(e) => {
warn!(error = %e, "Failed to generate auto-quant plan");
None
}
}
}
}
pub fn display_resolved_config(config: &ResolvedConfig) {
use console::style;
println!();
println!("{}", style("Auto Mode Resolution").bold().cyan());
println!("{}", style("--------------------").dim());
println!(
" Hardware: {} ({:.0} GB unified memory)",
style(&config.hardware.chip_model).bold(),
config.hardware.total_memory_gb(),
);
println!(" Model: {}", style(&config.fingerprint).bold(),);
println!(
" Method: {}",
style(&config.quant_method).green().bold(),
);
if config.bits < 16 {
println!(" Bits: {}", config.bits);
println!(" Group size: {}", config.group_size);
}
println!(" Confidence: {:.0}%", config.confidence * 100.0,);
println!(" Source: {}", config.source,);
println!(" Reasoning: {}", config.reasoning);
println!();
}
#[cfg(test)]
mod tests {
use super::*;
fn make_hardware() -> HardwareProfile {
HardwareProfile {
chip_model: "Apple M5 Max".to_string(),
total_memory_bytes: 128 * 1024 * 1024 * 1024,
available_memory_bytes: 100 * 1024 * 1024 * 1024,
performance_cores: 14,
efficiency_cores: 4,
total_cores: 18,
memory_bandwidth_gbs: 540.0,
}
}
fn make_fingerprint() -> ModelFingerprint {
ModelFingerprint {
architecture: "LlamaForCausalLM".to_string(),
total_params: 8_000_000_000,
layer_count: 32,
expert_count: 0,
attention_types: vec!["attention".to_string()],
hidden_size: 4096,
dtype: "bfloat16".to_string(),
intermediate_size: Some(14336),
num_attention_heads: 32,
num_kv_heads: Some(8),
vocab_size: 128256,
}
}
#[test]
fn test_resolve_without_ruvector_uses_heuristics() {
let hw = make_hardware();
let fp = make_fingerprint();
let result = AutoResolver::resolve(&hw, &fp, None).unwrap();
assert_eq!(result.source, ResolvedSource::Heuristic);
assert!(!result.quant_method.is_empty());
assert!(result.confidence > 0.0);
}
#[test]
fn test_resolved_config_has_hardware_and_fingerprint() {
let hw = make_hardware();
let fp = make_fingerprint();
let result = AutoResolver::resolve(&hw, &fp, None).unwrap();
assert_eq!(result.hardware.chip_model, "Apple M5 Max");
assert_eq!(result.fingerprint.architecture, "LlamaForCausalLM");
}
#[test]
fn test_resolved_source_display() {
assert_eq!(
format!("{}", ResolvedSource::RuVectorExact),
"stored (exact match)"
);
assert_eq!(format!("{}", ResolvedSource::Heuristic), "heuristic");
}
}